generate random integers with a specific sum - c#

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.

Related

How to split an array into intervals with given width and check count how many times, the values have appeared in each intervals C#?

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.

C# How to print/show the ''difference'' between all numbers in an Array and the average

I am new to c# and trying to learn the concept of arrays. In my program, I want to show the difference between all the numbers in the array and the average. I only managed to print 1 element(number) in the array with the difference.
If you try to run my program it is divided into 3 parts. the last part is where I am stuck. it only prints the last(19th) element/difference of the array instead of all the Elements.
Any tips are welcome :)
(FYI, I did not yet learn the concept of methods/functions.)
static void Main(string[] args)
{
int[] Elements = new int[20]; // this creates/declares an integer array with 20 Elements
double avg = 0, sum = 0, diff = 0; // declare average variable
for (int i = 0; i < Elements.Length; i++) // this is a loop to show the random numbers of elements
{
int Element = i; // declare the Element variable
Random rn = new Random();
int numbers = rn.Next(0, 200); // creates a number between 0 and 200
Console.WriteLine("Element {0} is {1}", Element, numbers); // print values
sum += numbers; // calculate the sum of numbers
avg = sum / Elements.Length; // calculate average of the sum
if (avg > numbers) // calculate diffrence
{
diff = avg - numbers;
}
else
{
diff = numbers - avg;
}
if (Element == 19)
{
Console.WriteLine("\n");
Console.WriteLine("The average is: {0}", avg);
Console.WriteLine("\n");
for (int z = 0; z < Elements.Length; z++)
{
Console.WriteLine("Diffrence between Element {0} and average is: {1}", Element, Math.Abs(diff));
}
}
}
Console.ReadKey();
}
I made sure the number was added to the array.
The creation of the Random instance should be outside the array otherwise the generated numbers won't be random.
Adjusted the last loop.
int[] Elements = new int[20]; // this creates/declares an integer array with 20 Elements
double avg = 0, sum = 0, diff = 0; // declare average variable
Random rn = new Random();
for (int i = 0; i < Elements.Length; i++) // this is a loop to show the random numbers of elements
{
int Element = i; // declare the Element variable
int numbers = rn.Next(0, 200); // creates a number between 0 and 200
Console.WriteLine("Element {0} is {1}", Element, numbers); // print values
Elements[Element] = numbers;
sum += numbers; // calculate the sum of numbers
avg = sum / Elements.Length; // calculate average of the sum
}
Console.WriteLine("\n");
Console.WriteLine("The average is: {0}", avg);
Console.WriteLine("\n");
for (int z = 0; z < Elements.Length; z++)
{
diff = Elements[z] - avg;
Console.WriteLine("Diffrence between Element {0} and average is: {1}", z, Math.Abs(diff));
}
Console.ReadKey();

Array Of Random Alternate Positive And Negative Number in C#

I would like to generate array of 10 elements with random number between (-10,10).
And scenario is array can contain positive number between (0,10) for odd positions
and contain negative number between(-10,0) for even position
For Example: 1,-2,3,-4,7,-1,3,-3,2,-9
And im totally stucked in generating -ve number at even places because im new in programming.
Any Help Is Appreciated,Thanx In Advance
Ok, i got your point. Try This Code
Random rand = new Random();
int[] arr = new int[10];
for(int i = 0; i < 10; i++)
{
if(i % 2 == 0)
arr[i] = rand.Next(1,10);
else
arr[i] = (rand.Next(1,10)) * -1;
}
for(int i = 0; i < 10; i++)
{
Console.WriteLine(arr[i]);
}
Sample Output
1
-9
9
-8
1
-4
2
-6
4
-9
Try this:
using System;
namespace ConsoleApp
{
class Program
{
static void Main (string[] args)
{
var random = new Random ();
var array = new int[10];
for (int i = 0; i < array.Length; i++)
{
bool isOdd = (i % 2) == 1; // check remainder
int randomNumber = random.Next (0, 11); // 0..10
if (isOdd) randomNumber = -randomNumber; // change sign
array[i] = randomNumber;
}
}
}
}
This is easier than all the posted answers so far. Here is a basic infinite loop from which to base your specific needs.
public IEnumerable<int> RandomAlternatingSequence()
{
var random = new Random();
int sign = -1;
while (true)
{
sign *= -1;
yield return sign * random.Next();
}
}
I realize that the post is 2 years old but this may help others who come looking.

Almost Ordered not sorting the exact amount of values i give it

this is a really easy question but i cant figure out a way around it. Apparently the almost ordered has a bug that it might randomize a little bit more than you ask it. the code is rather simple:
public void Section1Task1AlmostOrdered(int arraySize, int percentage)
{
int[] testArray = new int[arraySize];
Console.WriteLine("Ordered List: ");
for (int i = 1; i <= testArray.Length; i++)
{
testArray[i-1] = i;
Console.Write(i + "\t");
}
Console.WriteLine("Almost Ordered List: ");
testArray = shuffler.AlmostOrdered(arraySize, percentage);
for (int i = 0; i < testArray.Length; i++)
{
Console.Write(testArray[i] + "\t");
}
}
The shuffler is this part of the code:
public int[] AlmostOrdered(int n, double p)
{
if (p > 100)
{
throw new InvalidOperationException("Cannot shuffle more than 100% of the numbers");
}
int shuffled = 0;
//Create and Populate an array
int[] array = new int[n];
for(int i = 1; i <= n; i++)
{
array[i-1] = i;
}
//Calculate numbers to shuffle
int numsOutOfPlace = (int) Math.Ceiling(n * (p / 100));
int firstRandomIndex = 0;
int secondRandomIndex = 0;
do
{
firstRandomIndex = this.random.Next(n-1);
// to make sure that the two numbers are not the same
do
{
secondRandomIndex = this.random.Next(n - 1);
} while (firstRandomIndex == secondRandomIndex);
int temp = array[firstRandomIndex];
array[firstRandomIndex] = array[secondRandomIndex];
array[secondRandomIndex] = temp;
shuffled++;
}
while (shuffled < numsOutOfPlace);
return array;
}
When i enter values 10 for array size and 40 for percentage to be shuffled, it is shuffling 5 numbers instead of 4. Is there a way to perfect this method to make it more accurate?
Likely the problem is with the calculation:
int numsOutOfPlace = (int)Math.Ceiling(n * (p / 100));
So if p=40 and n=10, then in theory you should get 4. But you're dealing with floating point numbers. So if (p/100) returns 0.400000000001, then the result will be 4.000000001, and Math.Ceiling will round that up to 5.
You might want to replace Math.Ceiling with Math.Round and see how that works out.

Generate 30 Random Numbers from a given sum

I am working on a report in C# in which I am given a total number of prints a department printed and I have to distribute them according to the month date that on 1st March 10 papers are printed out and so on to 31st March.
I have a form which takes the total print outs count.
I have a Month Selector.
From Month I get the total days which is total numbers to be generated eg: 30 or 31 or 28
Scenario :
In the month of March 2000 Prints outs
Total Sum of Month : 2000
Numbers to be generated : 31
this is my code
int sum = 2345;
int nums = 23;
Random rand = new Random();
int newNum = 0;
int[] ar = new int[23];
for (int i = 0; i < nums; i++)
{
newNum = rand.Next(0, sum);
ar[i] = newNum;
sum = sum - newNum;
}
for (int i = 0; i < 23 ; i++)
{
Console.WriteLine(ar[i]);
}
Console.ReadLine();
what happens is in the ending numbers it goes to zero . I want Normally distributed like on one index it stores the maximum value at first and in the end it decreases.
We have a thrid party Ricoh Print/PhotoCopier Machine installed and third party bills us with certain amount which they have calculate that our department has printed 3000 printouts so we have to distribute them in the days randomly, print out the report and get payment invoice from our department head.
The department people are doing it on excel I offered them to give them a solution. Windows form application is built and I just have to put this logic thats all..
Thank you for your feedbacks
You can do this easily with partitions. For a 4 day month that produced 10 things: generate 3 random numbers between 0 and 10 (inclusive). Sort them and append 10 to the list of numbers. So we have perhaps:
3 6 6 10
Which partitions our prints:
p p p | p p p | | p p p p
If you want to have 23 random numbers with sum of 2345, you can use this code:
int sum = 2345;
int nums = 23;
int max = sum / nums;
Random rand = new Random();
int newNum = 0;
int[] ar = new int[23];
for (int i = 0; i < nums-1; i++) {
newNum = rand.Next(max);
ar[i] = newNum;
sum-= newNum;
max = sum / (nums-i-1);
}
ar[nums - 1] = sum;
It will give you:
Here is my idea for generating 30 random numbers with a specific sum:
int sum = 3000;
int size = 30; // assumes that (sum % size == 0)
int[] result = new int[size];
Random rand = new Random();
int x = sum / size;
for (int i = 0; i < size; i++)
{
result[i] = x;
}
for (int i = 0; i < x; i++)
{
var a = rand.Next(size - 1); // not sure if parameter is inclusive?
var b = rand.Next(size - 1); // should return number between 0 and size-1 inclusively
result[a]++;
result[b]--;
}
int testSum = result.Sum(); // will equal "sum" (3000)
Lee Daniel Crocker linked to this, though, which I think is a better solution. Very neat and intuitive.

Categories

Resources