I need to get a random number in C#, within (-15, 15) but without generate values between (-10, 10) as float numbers. Like the random should come with in (-15,-10) and (10,15).
Is it possible to get?
This can be done in one line, but I separated it out for clarity.
public double GetRandomNumber()
{
//Between 0 and 1
Random random = new Random();
double randomNumber = random.NextDouble();
//Between -0.5 and 0.5;
randomNumber -= 0.5;
//Between -5.0 and 5.0;
randomNumber *= 10.0;
//Between [-15.0, -10.0] or [10.0, 15.0]
randomNumber += Math.Sign(randomNumber) * 10.0;
return randomNumber;
}
One way would be to use this:
var random = new Random();
var result = random.NextDouble();
if(result < 0.5)
result = -15 + result * 10;
else
result = 5 + result * 10;
Random.NextDouble generates a number between 0.0 and 1.0.
If it is less than 0.5 we treat this as the indicator to create a negative number.
Another approach, assuming that you do want a decimal place in your result (since it isn't entirely clear).
Random rand = new Random();
var intValue = rand.Next(10,15);
var decimalValue = rand.NextDouble();
var sign = rand.Next(0,1);
if (sign == 0) sign = -1;
return (intValue + decimalValue) * sign;
Generate two random numbers. The first random number will choose whether you're in the -15 to -10 range, or the 10 to 15 range, the second - how far along in that range you want to be.
Try:
Random random = new Random();
float value = (float) (Math.Sign((random.NextDouble() - 1)) * (10 + 5 * random.NextDouble()));
static Random r = new Random(2);
static void Main(string[] args)
{
int d = r.Next(-15, 15);
while ((d >= -15 && d <= -10) || (d >= 10 && d <= 15))
Console.WriteLine(d);
Console.ReadLine();
}
If you want integers and you want it to be performant, I would use:
var rand = new Random();
int value = rand.Next(-5, 7);
return value > 0 ? value + 9 : value - 10;
I am assuming you want numbers from the ranges: [-15, -10] and [10, -15]. Adjust hard-coded values above appropriately. To make it more performant, there may be a way to do some bit arithmetic and avoid the comparison but I'll leave that for the comments.
Here is a static method to accomplish that:
public static int RandomRange(int min, int max, bool includeNegatives = false)
{
if( min >= max) throw new Exception("min can't be greater than max");
Random rdm = new Random();
int num = 0;
while (num < min) num = rdm.Next(max + 1);
return num * (rdm.Next() % 2 == 0 ? -1 : 1);
}
To call it:
var TenToFifteenNegatives = RandomRange(10, 15, true);
var TenToFifteenNoNegatives = RandomRange(10, 15);
Related
In my array, arr3 has 1000 numbers in it. I have to split this array into k subintervals of width differenceofMaxMin . How can I do that? Later I have to count how many times, the values in arr3 have matched to each interval. But I am stuck at creating intervals from array with a given width.
Any kind of help will be really appreciated!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
double[] Statistics1 = new double[500];
double[] Statistics2 = new double[500];
double Alpha1;
double Alpha2;
double RV1;
double RV2;
Random random = new Random();
public double RandomDoubleInclusive() //We are using this method because random.NextDouble() method gives random number
//between 0 and 1 where 0 is inclusive and 1 is exclusive.
//Since the value of probability lies between 0 and 1, both inclusive that's why we need
//to use this method.
{
double d = 0.0;
int i = 0;
do
{
d = random.NextDouble();
i = random.Next(2);
}
while (i == 1 && d > 0);
return d + i;
}
private void label3_Click(object sender, EventArgs e)
{
}
int i,j;
private void button1_Click(object sender, EventArgs e)
{
int SampleSize = Convert.ToInt32(textBox3.Text);
for ( i = 0; i<500;)
{
for (j = 0; j < 500;)
{
Alpha1 = RandomDoubleInclusive();
Alpha2 = RandomDoubleInclusive();
double LnPart = Math.Log(Alpha1);
double part1 = (-2) * LnPart;
double part2 = 2 * 3.14159 * Alpha2;
double CosPart = Math.Cos(part2);
double SinPart = Math.Sin(part2);
RV1 = Math.Sqrt(part1) * CosPart;
Statistics1[i] = RV1;
RV2 = Math.Sqrt(part1) * SinPart;
Statistics2[j] = RV2;
i++;
j++;
}
}
var myList = new List<double>();
myList.AddRange(Statistics1);
myList.AddRange(Statistics2);
double[] arr3 = myList.ToArray();
double Max = arr3.Max();
double Min = arr3.Min();
double differenceofMaxMin = Max - Min; //calculating size of width of interval
double k;
k = Math.Log(SampleSize,2) + 1; //calculating number of subintervals
}
}
I'm not sure I fully understand what exactly you're trying to achieve, but I can certainly try to help you out with an example on how to split an array arr3 into k subintervals with (max) number of elements differenceofMaxMin
var arr3 = Enumerable.Range(0, 1000);
// given: the max number of elements
var differenceofMaxMin = 300;
// determine the number of subintervals
// note that the last subinterval may contain less than differenceofMaxMin elements
var k = (int)Math.Ceiling((double)arr3.Count() / differenceofMaxMin);
var arr3_split = Enumerable.Range(0, k)
.Select(i => arr3.Skip(i * differenceofMaxMin).Take(differenceofMaxMin));
Looking at your method to generate a random double from [0, 1], I think it's overkill since the likelihood to actually draw exactly 1.0 is extremely low.
Here's a bit of a puzzler: Random.Next() has an overload that accepts a minimum value and a maximum value. This overload returns a number that is greater than or equal to the minimum value (inclusive) and less than the maximum value (exclusive).
I would like to include the entire range including the maximum value. In some cases, I could accomplish this by just adding one to the maximum value. But in this case, the maximum value can be int.MaxValue, and adding one to this would not accomplish what I want.
So does anyone know a good trick to get a random number from int.MinValue to int.MaxValue, inclusively?
UPDATE:
Note that the lower range can be int.MinValue but can also be something else. If I know it would always be int.MinValue then the problem would be simpler.
The internal implementation of Random.Next(int minValue, int maxValue) generates two samples for large ranges, like the range between Int32.MinValue and Int32.MaxValue. For the NextInclusive method I had to use another large range Next, totaling four samples. So the performance should be comparable with the version that fills a buffer with 4 bytes (one sample per byte).
public static class RandomExtensions
{
public static int NextInclusive(this Random random, int minValue, int maxValue)
{
if (maxValue == Int32.MaxValue)
{
if (minValue == Int32.MinValue)
{
var value1 = random.Next(Int32.MinValue, Int32.MaxValue);
var value2 = random.Next(Int32.MinValue, Int32.MaxValue);
return value1 < value2 ? value1 : value1 + 1;
}
return random.Next(minValue - 1, Int32.MaxValue) + 1;
}
return random.Next(minValue, maxValue + 1);
}
}
Some results:
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(24917099).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
var random = new Random(784288084);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue
Update: My implementation has mediocre performance for the largest possible range (Int32.MinValue - Int32.MaxValue), so I came up with a new one that is 4 times faster. It produces around 22,000,000 random numbers per second in my machine. I don't think that it can get any faster than that.
public static int NextInclusive(this Random random, int minValue, int maxValue)
{
if (maxValue == Int32.MaxValue)
{
if (minValue == Int32.MinValue)
{
var value1 = random.Next() % 0x10000;
var value2 = random.Next() % 0x10000;
return (value1 << 16) | value2;
}
return random.Next(minValue - 1, Int32.MaxValue) + 1;
}
return random.Next(minValue, maxValue + 1);
}
Some results:
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue
new Random(1655705829).NextInclusive(int.MinValue, int.MaxValue); // = int.MaxValue
var random = new Random(1704364573);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // = int.MinValue
No casting, no long, all boundary cases taken into account, best performance.
static class RandomExtension
{
private static readonly byte[] bytes = new byte[sizeof(int)];
public static int InclusiveNext(this Random random, int min, int max)
{
if (max < int.MaxValue)
// can safely increase 'max'
return random.Next(min, max + 1);
// now 'max' is definitely 'int.MaxValue'
if (min > int.MinValue)
// can safely decrease 'min'
// so get ['min' - 1, 'max' - 1]
// and move it to ['min', 'max']
return random.Next(min - 1, max) + 1;
// now 'max' is definitely 'int.MaxValue'
// and 'min' is definitely 'int.MinValue'
// so the only option is
random.NextBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
}
Well, I have a trick. I'm not sure I'd describe it as a "good trick", but I feel like it might work.
public static class RandomExtensions
{
public static int NextInclusive(this Random rng, int minValue, int maxValue)
{
if (maxValue == int.MaxValue)
{
var bytes = new byte[4];
rng.NextBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
return rng.Next(minValue, maxValue + 1);
}
}
So, basically an extension method that will simply generate four bytes if the upper-bound is int.MaxValue and convert to an int, otherwise just use the standard Next(int, int) overload.
Note that if maxValue is int.MaxValue it will ignore minValue. Guess I didn't account for that...
Split the ranges in two, and compensate for the MaxValue:
r.Next(2) == 0 ? r.Next(int.MinValue, 0) : (1 + r.Next(-1, int.MaxValue))
If we make the ranges of equal size, we can get the same result with different math. Here we rely on the fact that int.MinValue = -1 - int.MaxValue:
r.Next(int.MinValue, 0) - (r.Next(2) == 0 ? 0 : int.MinValue)
I'd suggest using System.Numerics.BigInteger like this:
class InclusiveRandom
{
private readonly Random rnd = new Random();
public byte Next(byte min, byte max) => (byte)NextHelper(min, max);
public sbyte Next(sbyte min, sbyte max) => (sbyte)NextHelper(min, max);
public short Next(short min, short max) => (short)NextHelper(min, max);
public ushort Next(ushort min, ushort max) => (ushort)NextHelper(min, max);
public int Next(int min, int max) => (int)NextHelper(min, max);
public uint Next(uint min, uint max) => (uint)NextHelper(min, max);
public long Next(long min, long max) => (long)NextHelper(min, max);
public ulong Next(ulong min, ulong max) => (ulong)NextHelper(min, max);
private BigInteger NextHelper(BigInteger min, BigInteger max)
{
if (max <= min)
throw new ArgumentException($"max {max} should be greater than min {min}");
return min + RandomHelper(max - min);
}
private BigInteger RandomHelper(BigInteger bigInteger)
{
byte[] bytes = bigInteger.ToByteArray();
BigInteger random;
do
{
rnd.NextBytes(bytes);
bytes[bytes.Length - 1] &= 0x7F;
random = new BigInteger(bytes);
} while (random > bigInteger);
return random;
}
}
I tested it with sbyte.
var rnd = new InclusiveRandom();
var frequency = Enumerable.Range(sbyte.MinValue, sbyte.MaxValue - sbyte.MinValue + 1).ToDictionary(i => (sbyte)i, i => 0ul);
var count = 100000000;
for (var i = 0; i < count; i++)
frequency[rnd.Next(sbyte.MinValue, sbyte.MaxValue)]++;
foreach (var i in frequency)
chart1.Series[0].Points.AddXY(i.Key, (double)i.Value / count);
chart1.ChartAreas[0].AxisY.StripLines
.Add(new StripLine { Interval = 0, IntervalOffset = 1d / 256, StripWidth = 0.0003, BackColor = Color.Red });
Distribution is OK.
This is guaranteed to work with negative and non-negative values:
public static int NextIntegerInclusive(this Random r, int min_value, int max_value)
{
if (max_value < min_value)
{
throw new InvalidOperationException("max_value must be greater than min_value.");
}
long offsetFromZero =(long)min_value; // e.g. -2,147,483,648
long bound = (long)max_value; // e.g. 2,147,483,647
bound -= offsetFromZero; // e.g. 4,294,967,295 (uint.MaxValue)
bound += Math.Sign(bound); // e.g. 4,294,967,296 (uint.MaxValue + 1)
return (int) (Math.Round(r.NextDouble() * bound) + offsetFromZero); // e.g. -2,147,483,648 => 2,147,483,647
}
It's actually interesting that this isn't the implementation for Random.Next(int, int), because you can derive the behavior of exclusive from the behavior of inclusive, but not the other way around.
public static class RandomExtensions
{
private const long IntegerRange = (long)int.MaxValue - int.MinValue;
public static int NextInclusive(this Random random, int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException(nameof(minValue));
}
var buffer = new byte[4];
random.NextBytes(buffer);
var a = BitConverter.ToInt32(buffer, 0);
var b = a - (long)int.MinValue;
var c = b * (1.0 / IntegerRange);
var d = c * ((long)maxValue - minValue + 1);
var e = (long)d + minValue;
return (int)e;
}
}
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(-451732719).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
new Random(-394328071).NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue
As I understand it you want Random to put out a value between -2.147.483.648 and +2.147.483.647. But the problem is that Random given those values will only give values from -2.147.483.648 to +2.147.483.646, as the maximum is exclusive.
Option 0: Take the thing away and learn to do without it
Douglas Adams was not a Programmer AFAIK, but he has some good advice for us: "The technology involved in making anything invisible is so infinitely complex that nine hundred and ninety-nine billion, nine hundred and ninety-nine million, nine hundred and ninety-nine thousand, nine hundred and ninety-nine times out of a trillion it is much simpler and more effective just to take the thing away and do without it."
This might be such a case.
Option 1: We need a bigger Random!
Random.Next() uses Int32 as Argument. One option I can think off would be to use a different Random Function Which can take the next higher level of Integers (Int64) as input. An Int32 is implicitly cast into an Int64. Int64 Number = Int64(Int32.MaxValue)+1;
But afaik, you would have to go outside of .NET libraries to do this. At that point, you might as well look for a Random that is inclusive of the Max.
But I think there is a mathematical reason it had to exclude one value.
Option 2: Roll more
Another way is to use two calls of Random - each for one half of the range - and then add them.
Number1 = rng.Next(-2.147.483.648, 0);
Number2 = rng.Next(0, 2.147.483.647);
resut = Number1 + Number2;
However, I am 90% certain that will ruin the random distribution. My P&P RPG experience gave me some experience with dice chances and I know for a fact rolling 2 dice (or the same 2 times) will get you a very different result distribution than one specific die. If you do not need this random distribution, that is an option. But if you do not care too much about the distribution it is worth a check.
Option 3: Do you need the full range? Or do you just care for min and max to be in it?
I assume you are doing some form of testing and you need both Int.MaxValue and Int.MinValue to be in the range. But do you need every value in between as well, or could you do without one of them?
If you have to lose value, would you prefer loosing 4 rather then Int.MaxValue?
Number = rng.Next(Int.MinValue, Int.MaxValue);
if(Number > 3)
Number = Number +1;
code like this would get you every number between MinValue and MaxValue, except 4. But in most cases code that can deal with 3 and 5 can also deal with 4. There is no need to explicitly test 4.
Of course, that assumes 4 is not some important test number that has to be run (I avoided 1 and 0 for those reasons). You could also decide the number to "skip" Randomly:
skipAbleNumber = rng.Next(Int.MinValue +1, Int.MaxValue);
And then use > skipAbleNumber rather than > 4.
You can not use Random.Next() to achieve what you want, because you can not correspond sequence of N numbers to N+1 and not miss one :). Period.
But you can use Random.NextDouble(), which returns double result:
0 <= x < 1 aka [0, 1)
between 0, where [ is inclusive sign and ) exclusive
How do we correspond N numbers to [0, 1)?
You need to split [0, 1) to N equal segments:
[0, 1/N), [1/N, 2/N), ... [N-1/N, 1)
And here is where it becomes important that one border is inclusive and another is exclusive - all N segments are absolutely equal!
Here is my code: I made it as a simple console program.
class Program
{
private static Int64 _segmentsQty;
private static double _step;
private static Random _random = new Random();
static void Main()
{
InclusiveRandomPrep();
for (int i = 1; i < 20; i++)
{
Console.WriteLine(InclusiveRandom());
}
Console.ReadLine();
}
public static void InclusiveRandomPrep()
{
_segmentsQty = (Int64)int.MaxValue - int.MinValue;
_step = 1.0 / _segmentsQty;
}
public static int InclusiveRandom()
{
var randomDouble = _random.NextDouble();
var times = randomDouble / _step;
var result = (Int64)Math.Floor(times);
return (int)result + int.MinValue;
}
}
This method can give you a random integer within any integer limits. If the maximum limit is less than int.MaxValue, then it uses the ordinary Random.Next(Int32, Int32) but with adding 1 to upper limit to include its value. If not but with lower limit greater than int.MinValue, it lowers the lower limit with 1 to shift the range 1 less that add 1 to the result. Finally, if both limits are int.MinValue and int.MaxValue, it generates a random integer 'a' that is either 0 or 1 with 50% probability of each, then it generates two other integers, the first is between int.MinValue and -1 inclusive, 2147483648 values, and the second is between 0 and int.MaxValue inclusive , 2147483648 values also, and using them with the value of 'a' it select an integer with totally equal probability.
private int RandomInclusive(int min, int max)
{
if (max < int.MaxValue)
return Random.Next(min, max + 1);
if (min > int.MinValue)
return Random.Next(min - 1, max) + 1;
int a = Random.Next(2);
return Random.Next(int.MinValue, 0) * a + (Random.Next(-1, int.MaxValue) + 1) * (1 - a);
}
What about this?
using System;
public class Example
{
public static void Main()
{
Random rnd = new Random();
int min_value = max_value;
int max_value = min_value;
Console.WriteLine("\n20 random integers from 10 to 20:");
for (int ctr = 1; ctr <= 20; ctr++)
{
Console.Write("{0,6}", rnd.Next(min_value, max_value));
if (ctr % 5 == 0) Console.WriteLine();
}
}
}
You can try this. A bit hacky but can get you both min and max inclusive.
static void Main(string[] args)
{
int x = 0;
var r = new Random();
for (var i = 0; i < 32; i++)
x = x | (r.Next(0, 2) << i);
Console.WriteLine(x);
Console.ReadKey();
}
You can add 1 to generated number randomly so it still random and cover full range integer.
public static class RandomExtension
{
public static int NextInclusive(this Random random, int minValue, int maxValue)
{
var randInt = random.Next(minValue, maxValue);
var plus = random.Next(0, 2);
return randInt + plus;
}
}
Will this work for you?
int random(Random rnd, int min, int max)
{
return Convert.ToInt32(rnd.NextDouble() * (max - min) + min);
}
I have 5 fields, I want them all to have a generated number between 0 and 100. But, the sum of the 5 fields should be 100.
When I want to give a random number for one field I would do the following:
Random rnd = new Random();
int x= rnd.Next(1, 10);
But how should I do that for multiple fields that needs to have a sum of 100 together?
You can use the following approach:
generate 4 random integers in [0, 100]
sort them, let's denote the sorted values as 0 ≤ x1 ≤ x2 ≤ x3 ≤ x4 ≤ 100
use the following 5 values as the random numbers with sum 100:
N1 = x1
N2 = x2 - x1
N3 = x3 - x2
N4 = x4 - x3
N5 = 100 - x4
It basically corresponds to randomly choosing 4 sectioning points on the [0, 100] interval, and using the lengths of the 5 resulting intervals as the random numbers:
const int k = 5;
const int sum = 100;
Random rnd = new Random();
int[] x = new int[k + 1];
// the endpoints of the interval
x[0] = 0;
x[k] = sum;
// generate the k - 1 random sectioning points
for (int i = 1; i < k; i++) {
x[i] = rnd.Next(0, sum + 1);
}
// sort the sectioning points
Array.Sort(x);
// obtain the k numbers with sum s
int[] N = new int[k];
for (int i = 0; i < k; i++) {
N[i] = x[i + 1] - x[i];
}
In order to make your distribution uniform, you could try the following aproach:
Generate some random numbers.
Normalize them.
Correct the last field to get exactly the expected sum, if needed.
The code:
const int ExpectedSum = 100;
Random rnd = new Random();
int[] fields = new int[5];
// Generate 4 random values and get their sum
int sum = 0;
for (int i = 0; i < fields.Length - 1; i++)
{
fields[i] = rnd.Next(ExpectedSum);
sum += fields[i];
}
// Adjust the sum as if there were 5 random values
int actualSum = sum * fields.Length / (fields.Length - 1);
// Normalize 4 random values and get their sum
sum = 0;
for (int i = 0; i < fields.Length - 1; i++)
{
fields[i] = fields[i] * ExpectedSum / actualSum;
sum += fields[i];
}
// Set the last value
fields[fields.Length - 1] = ExpectedSum - sum;
Live example: https://dotnetfiddle.net/5yXwOP
To achieve a truly random distribution, with every element having the chance to be 100 with a total sum of 100, you can use the following solution:
public static int[] GetRandomDistribution(int sum, int amountOfNumbers)
{
int[] numbers = new int[amountOfNumbers];
var random = new Random();
for (int i = 0; i < sum; i++)
{
numbers[random.Next(0, amountOfNumbers)]++;
}
return numbers;
}
static void Main(string[] args)
{
var result = GetRandomDistribution(100, 5);
}
It increases a random number by one until the sum is reached. This should fulfill all your criterias.
After thinking about it, I prefer the following solution, because it's less likely to generate an equal distribution:
public static int[] GetRandomDistribution2(int sum, int amountOfNumbers)
{
int[] numbers = new int[amountOfNumbers];
var random = new Random();
for (int i = 0; i < amountOfNumbers; i++)
{
numbers[i] = random.Next(sum);
}
var compeleteSum = numbers.Sum();
// Scale the numbers down to 0 -> sum
for (int i = 0; i < amountOfNumbers; i++)
{
numbers[i] = (int)(((double)numbers[i] / compeleteSum) * sum);
}
// Due to rounding the number will most likely be below sum
var resultSum = numbers.Sum();
// Add +1 until we reach "sum"
for (int i = 0; i < sum - resultSum; i++)
{
numbers[random.Next(0, amountOfNumbers)]++;
}
return numbers;
}
For Example.
int sum=100;
int i = 5;
Random rnd = new Random();
while (true)
{
int cur;
--i;
if (i == 0) {
Console.WriteLine(sum + " ");
break;
} else
cur=rnd.Next(1, sum);
sum -= cur;
Console.WriteLine(cur + " ");
}
Live Example: https://dotnetfiddle.net/ltIK40
or
Random rnd = new Random();
int x= rnd.Next(1, 10);
int y= rnd.Next(x,x+10);
int y2=rnd.Next(y,y+10);
int y3=rnd.Next(y2,y2+10);
int y4=100-(x+y+y2+y3);
My approach is this:
var rnd = new Random();
var numbers = Enumerable.Range(0, 5).Select(x => rnd.Next(0, 101)).ToArray().OrderBy(x => x).ToArray();
numbers = numbers.Zip(numbers.Skip(1), (n0, n1) => n1 - n0).ToArray();
numbers = numbers.Concat(new[] { 100 - numbers.Sum() }).ToArray();
This is as uniform as I think is possible.
Create your first random number. After that you take the difference between the value of num1 and 100 as the max def of rnd. But to guarantee that their sum is 100, you have to check at the last num if the sum of all nums is 100. If not the value of your last num is the difference that their sum and 100.
And to simply your code and get a clean strcuture, put that code in a loop and instead of single numbers work with an int[5] array.
private int[] CreateRandomNumbersWithSum()
{
int[] nums = new int[5];
int difference = 100;
Random rnd = new Random();
for (int i = 0; i < nums.Length; i++)
{
nums[i] = rnd.Next(0, difference);
difference -= nums[i];
}
int sum = 0;
foreach (var num in nums)
sum += num;
if (sum != 100)
{
nums[4] = 100 - sum;
}
return nums;
}
I think this is a very simple solution:
public void GenerateRandNr(int total)
{
var rnd = new Random();
var nr1 = rnd.Next(0, total);
var nr2 = rnd.Next(0, total - nr1);
var nr3 = rnd.Next(0, total - nr1 - nr2);
var nr4 = rnd.Next(0, total - nr1 - nr2 - nr3);
var nr5 = total - nr1 - nr2 - nr3 - nr4;
}
EDIT:
Just tested it, works fine for me:
The solution is that it's not the numbers that need to be random so much as the distribution needs to be random. The randomness of the numbers will be a side effect of their random distribution.
So you would start with five random numbers in a given range. The exact range doesn't matter as long as the range is the same for all five, although a broader range allows for more variation. I'd use Random.NextDouble() which returns random numbers between 0 and 1.
Each of those individual numbers divided by the sum of those numbers represents a distribution.
For example, say your random numbers are .4, .7, .2, .5, .2. (Using fewer digits for simplicity.)
The total of those numbers is 2. So now the distributions are each of those numbers divided by the total.
.4 / 2 = .20
.7 / 2 = .35
.2 / 2 = .10
.5 / 2 = .25
.2 / 2 = .10
You'll notice that those distributions will equal 100% or really close to it if there are a lot more decimal places.
The output is going to be each of those distributions times the target number, in this case, 100. In other words, each of those numbers represents a piece of 100.
So multiplying each of those distributions times the target, we get 20, 35, 10, 25, and 100, which add up to 100.
The trouble is that because of rounding your numbers won't always perfectly add up to 100. To fix that you might add one to the smallest number if the sum is less than 100, or subtract one from the largest number of the the sum is greater than 100. Or you could choose to add or subtract on one of the numbers at random.
Here's a class to create the distributions. (I'm just playing around so I haven't exactly optimized this to death.)
public class RandomlyDistributesNumbersTotallingTarget
{
public IEnumerable<int> GetTheNumbers(int howManyNumbers, int targetTotal)
{
var random = new Random();
var distributions = new List<double>();
for (var addDistributions = 0; addDistributions < howManyNumbers; addDistributions++)
{
distributions.Add(random.NextDouble());
}
var sumOfDistributions = distributions.Sum();
var output = distributions.Select(
distribution =>
(int)Math.Round(distribution / sumOfDistributions * targetTotal, 0)).ToList();
RoundUpOutput(output, targetTotal);
return output;
}
private void RoundUpOutput(List<int> output, int targetTotal)
{
var difference = targetTotal - output.Sum();
if (difference !=0)
{
var indexToAdjust =
difference > 0 ? output.IndexOf(output.Min()) : output.IndexOf(output.Max());
output[indexToAdjust]+= difference;
}
}
}
And here's a not-perfectly-scientific unit test that tests it many times over and ensures that the results always total 100.
[TestMethod]
public void OutputTotalsTarget()
{
var subject = new RandomlyDistributesNumbersTotallingTarget();
for (var x = 0; x < 10000; x++)
{
var output = subject.GetTheNumbers(5, 100);
Assert.AreEqual(100, output.Sum());
}
}
Some sample outputs:
5, 30, 27, 7, 31
15, 7, 26, 27, 25
10, 11, 23, 2, 54
The numbers are always going to average to 20, so while 96, 1, 1, 1 is a hypothetical possibility they're going to tend to hover closer to 20.
Okay. Having been burned by my previous attempt at this seemingly trivial problem, I decided to have another go. Why not normalise all the numbers after generation? This guarantees randomness, and avoids O(n log n) performance from a sort. It also has the advantage that even with my basic maths, I can work out that the numbers are uniformly distributed.
public static int[] UniformNormalization(this Random r, int valueCount, int valueSum)
{
var ret = new int[valueCount];
long sum = 0;
for (int i = 0; i < valueCount; i++)
{
var next = r.Next(0, valueSum);
ret[i] = next;
sum += next;
}
var actualSum = 0;
for (int i = 0; i < valueCount; i++)
{
actualSum += ret[i] = (int)((ret[i] * valueSum) / sum);
}
//Fix integer rounding errors.
if (valueSum > actualSum)
{
for (int i = 0; i < valueSum - actualSum; i++)
{
ret[r.Next(0, valueCount)]++;
}
}
return ret;
}
This should also be one of the fastest solutions.
How to generate random value from 5 to 10 with step 0.4?
I tried:
var numberOfSteps = 0.4;
var _step = (maximum - minimum) / numberOfSteps;
var difference = maximum - minimum;
var stepsToDifference = Math.Round(difference / _step);
return Convert.ToString(minimum + _step * stepsToDifference);
Do you mean you want the number to be 5, 5.4, 5.8, ... 10? As 5 and 10 are 5 apart and not evenly divisible by .4, you will have to either stop at 9.8 or at 10.2 or stop at 10 with a short step at the last.
But here is how you can get a random number that is a member of that set (will be any number from 5 to 9.8 on the even .4 step).
Random rand = new Random();
float randomInSet = ((float)rand.Next(0,12)*.4)+5;
Please let me know if this is not what you had in mind.
How about something like this:
Random rand = new Random();
int intPart = 0;
int multiplesOfPointFour = 0;
bool doAgain = true;
double finalValue = 0.0;
while (doAgain)
{
intPart = rand.Next(5, 8);
multiplesOfPointFour = rand.Next(0, 6);
finalValue = intPart + (multiplesOfPointFour * 0.4);
Console.WriteLine("Value is: " + finalValue.ToString());
Console.Write("Press any key to go again...");
Console.ReadKey();
}
So we pick an integer between 5 and 10 and then add 0.4 a random number of times between 0 and 12 to that integer, because 5.0 / 0.4 = 12.5
Was trying to generate a random Hue to create really different colors, so here it is:
//***************************************************************************
public static partial class RndExtensions
//***************************************************************************
{
//-------------------------------------------------------------
public static double NextDouble(this Random rnd, double from, double to, double step)
//-------------------------------------------------------------
{
var delta = to - from;
var nbOfSteps = (int)(delta / step);
var randomStep = rnd.Next(0, nbOfSteps);
return step*randomStep + from;
}
//-------------------------------------------------------------
public static double Next(this Random rnd, int from, int to, int step)
//-------------------------------------------------------------
{
var delta = to - from;
var nbOfSteps = (int)(delta / step);
var randomStep = rnd.Next(0, nbOfSteps);
return step * randomStep + from;
}
}
Used as
Random rnd = new Random();
var randomHue = rnd.NextDouble(0, 359, 25);
I was following a tutorial on random number generation in Visual C# 2010 Express as a console app and ran into an issue.
The code matches the tutorial I was watching as far as I can tell with 5 overviews.
The idea is that it will take built in random seed function and generate 100 values with it. The program will be storing the random number (a double) as a KEY for a DICTIONARY and a int VALUE to show the number of duplicates. This system will print analytical data about the duplicates, the mean, and the distribution.
//PROGRAM SCOPE
private const int Count = 100;
// MAIN CLASS SCOPE
var standardRnd = new Random(20);
var list = new Dictionary<double,int>();
//In a for loop till Count-1
var rand = standardRnd.NextDouble();
if (!list.ContainsKey(rand))
list.Add(rand, 1);
else
{
list[rand]++;
duplicate++;
}
When I run the program the console will activate just fine but I wont print anything to the screen. After a second or so It will throw an exception and highlight this code snippet.
sum += rand * list[rand];
ERROR: The given key was not present in the dictionary.
Logically it makes sense to me. Do you guys see anything wonky?
Thank you for any help you can give.
FULL CODE:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Random_RnD
{
class Program
{
static void Main(string[] args)
{
var standardRnd = new Random(20);
var min = 1.0;
var max = 0.0;
var valueCounter = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var list = new Dictionary<double,int>();
var duplicate = 0;
var iterations = 0;
var timeStart = DateTime.Now;
//Do Stuff
for (int x = 0; x < Count; x++)
{
var rand = standardRnd.NextDouble();
if (!list.ContainsKey(rand))
list.Add(rand, 1);
else
{
list[rand]++;
duplicate++;
}
iterations++;
}
var timeStop = DateTime.Now;
var elapseTime = TimeSpan.FromTicks((timeStop-timeStart).Ticks);
//Analytics
var sum = 0.0;
foreach (var kvp in list)
{
var rand = kvp.Key;
if (rand < min)
min = rand;
if(rand>max)
rand = max;
if (rand >= 0.0 && rand < 0.1)
valueCounter[0]++;
if (rand >= 0.1 && rand < 0.2)
valueCounter[1]++;
if (rand >= 0.2 && rand < 0.3)
valueCounter[2]++;
if (rand >= 0.3 && rand < 0.4)
valueCounter[3]++;
if (rand >= 0.4 && rand < 0.5)
valueCounter[4]++;
if (rand >= 0.5 && rand < 0.6)
valueCounter[5]++;
if (rand >= 0.6 && rand < 0.7)
valueCounter[6]++;
if (rand >= 0.7 && rand < 0.8)
valueCounter[7]++;
if (rand >= 0.8 && rand < 0.9)
valueCounter[8]++;
if (rand >= 0.9 && rand <= 1.0)
valueCounter[9]++;
sum += rand * list[rand];
}
Console.WriteLine("{0:###,###,###} Iteration took: {1:D2}min:{2:D2}sec:{3:D2}msec",
iterations, elapseTime.Minutes, elapseTime.Seconds, elapseTime.Milliseconds);
Console.WriteLine(" {0}\n {1}", min, max);
Console.WriteLine(" 0.0 to 0.1 = {0:###,###,###}", valueCounter[0]);
Console.WriteLine(" 0.1 to 0.2 = {0:###,###,###}", valueCounter[1]);
Console.WriteLine(" 0.2 to 0.3 = {0:###,###,###}", valueCounter[2]);
Console.WriteLine(" 0.3 to 0.4 = {0:###,###,###}", valueCounter[3]);
Console.WriteLine(" 0.4 to 0.5 = {0:###,###,###}", valueCounter[4]);
Console.WriteLine(" 0.5 to 0.6 = {0:###,###,###}", valueCounter[5]);
Console.WriteLine(" 0.6 to 0.7 = {0:###,###,###}", valueCounter[6]);
Console.WriteLine(" 0.7 to 0.8 = {0:###,###,###}", valueCounter[7]);
Console.WriteLine(" 0.8 to 0.9 = {0:###,###,###}", valueCounter[8]);
Console.WriteLine(" 0.9 to 1.0 = {0:###,###,###}", valueCounter[9]);
var avg = sum / (double)iterations;
Console.WriteLine("\nStatistics:");
Console.WriteLine(" Mean = {0}", avg);
Console.WriteLine(" Duplicates = {0}", duplicate);
Console.ReadKey();
}
}
}
The problem is here:
if (rand>max)
rand = max;
This assigns 0 to rand. But 0 isn't guaranteed to be in the dictionary.
You meant max = rand;.
There is also a much easier way to find the minimum and maximum values in a collection by using LINQ:
var keys = list.Keys;
double min = keys.Min();
double max = keys.Max();