Reputation: 298
I want to generate 100 random numbers between 1 and 10. But the average of those 100 random numbers should be 7. How can I do that? I am doing as follows:
//generating random number
Random random = new Random();
int value = random.Next(1,10);
And storing each value in an array. If the average of 100 items in the array is not 7 then I need to get another 100 random numbers. Can anyone suggest a better way of doing this?
Upvotes: 13
Views: 5551
Reputation: 971
class Program
{
static void Main(string[] args)
{
var rnd = new Random();
var min = 1;
var max = 20;
var avg = 15;
var count = 5000;
var numbers = new List<int>();
for (var i = 0; i < count; i++)
{
var random1 = rnd.Next(min, avg + 1);
var random2 = rnd.Next(avg + 2, max + 1);
var randoms = new List<int>();
randoms.AddRange(Enumerable.Repeat<int>(random2, avg - min));
randoms.AddRange(Enumerable.Repeat<int>(random1, max - avg));
var generatedNumber = randoms[rnd.Next(randoms.Count)];
numbers.Add(generatedNumber);
}
numbers = numbers.OrderBy(x => x).ToList();
var groups = numbers.GroupBy(x => x).OrderByDescending(x => x.Count()).ToList();
groups.ForEach(x => Console.WriteLine($"{x.Key}: {x.Count()}"));
Console.WriteLine($"Average: {numbers.Average(x => x)}");
Console.WriteLine($"Count of numbers: {groups.Count}");
}
}
Upvotes: 0
Reputation: 4206
this function is for get fixed average between n Records randomly. that here in my answer "n" is declared as "count". https://github.com/amingolmahalle/RandomGenerateDataBetweenTwoNumber
public void ProccessGenerateData(WorkBook workBookData, out List<double> nomreList, out int adjustmentsVal)
{
try
{
nomreList = new List<double>();
adjustmentsVal = 0;
int count = workBookData.NumberStudents;
double min = workBookData.Min;
double max = workBookData.Max;
double target = workBookData.FixedAvg;
double tolerance = workBookData.Tolerance;
double minAverage = Math.Round(target - tolerance, 2);
double maxAverage = Math.Round(target + tolerance, 2);
Random r = new Random(DateTime.Now.Millisecond);
List<double> listNomre = new List<double>();
double sum = 0;
for (int i = 0; i < count; i++)
{
double d = Math.Round(RangedDouble(min, max, r), 2);
listNomre.Add(d);
sum += d;
sum = Math.Round(sum, 2);
}
int adjustments = 0;
while (Math.Round((sum / count), 2) < minAverage || Math.Round((sum / count), 2) > maxAverage)
{
if (Math.Round((sum / count), 2) < minAverage)
{
double oldDbl1 = listNomre.First(d => d < minAverage);
//min<a1+x1<max --> a1 is oldDbl1 , x1 --> Unknown
double newDbl1 = Math.Round(oldDbl1 + RangedDouble(min-oldDbl1, max - oldDbl1, r), 2);
listNomre.Remove(oldDbl1);
sum -= oldDbl1;
sum = Math.Round(sum, 2);
listNomre.Add(newDbl1);
sum += newDbl1;
sum = Math.Round(sum, 2);
adjustments++;
continue;
}
double oldDbl = listNomre.First(d => d > maxAverage);
//min<a1-x1<max --> a1 is oldDbl , x1 --> Unknown
double newDbl = Math.Round(oldDbl - RangedDouble(oldDbl-max, oldDbl - min, r), 2);
listNomre.Remove(oldDbl);
sum -= oldDbl;
sum = Math.Round(sum, 2);
listNomre.Add(newDbl);
sum += newDbl;
sum = Math.Round(sum, 2);
adjustments++;
}
nomreList = listNomre;
adjustmentsVal = adjustments;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
}
private static double RangedDouble(double min, double max, Random r)
{
//Function RangedDouble => Random Number Between 2 Double Numbers
//Random.NextDouble => returns a double between 0 and 1
return Math.Round( r.NextDouble() * (max - min) + min,2);
}
Upvotes: 0
Reputation: 2736
Like the other answers have posted, since we know the length we can get the average by just focusing on the total sum.
I'd solve it recursively. In the base case, we need to generate a list of length 1 which sums to some number s
. That's easy: the list just contains s
:
rand 1 s = [s]
Now we can solve the recursive case rand n s
where n
is the desired list length and s
is the desired sum. To do this, we'll generate two lists x
and y
and concatenate them together, subject to the given constraints:
length x + length y = n
sum x + sum y = s
1 * length x <= sum x -- Minimum value is 1
10 * length x >= sum x -- Maximum value is 10
1 * length y <= sum y
10 * length y >= sum y
These equations/inequalities can't be solved yet, so the first thing we do is choose the length of the lists. To keep down the level of recursion we can choose lx = round (n / 2)
then set the following:
length x = lx
length y = n - lx = ly
Hence:
sum x + sum y = s
1 * lx <= sum x
10 * lx >= sum x
1 * ly <= sum y
10 * ly >= sum y
We use the first equation to rewrite the inequalities:
1 * lx <= sum x
10 * lx >= sum x
1 * ly <= s - sum x
10 * ly >= s - sum x
We can rearrange the bottom two to make sum x
the subject:
sum x + 1 * ly <= s
sum x + 10 * ly >= s
sum x <= s - 1 * ly
sum x >= s - 10 * ly
We know ly
and s
so these give us definite bounds for sum x
, which we combine by taking the largest lower bound and the smallest upper bound:
max (1 * lx) (s - 10 * ly) <= sum x
min (10 * lx) (s - 1 * ly) >= sum x
These bounds make sense: they take into account the cases where every element in x
is 1 or 10 and they ensure that the remainder can be handled by sum y
. Now we just generate a random number B
between these bounds, then set:
sum x = B
sum y = s - B
From that, we can perform our recursion (assuming some random number function randInt
):
rand n s = let lx = round (n / 2)
ly = n - lx
lower = max (1 * lx) (s - 10 * ly)
upper = min (10 * lx) (s - 1 * ly)
b = randInt lower upper
in rand lx b ++ rand ly (s - b)
Now your list can be generated by calling:
myList = rand 100 700
I've written this in Haskell for brevity, but it's just arithmetic so should translate to C# easily. Here's a Python version if it helps:
def rand(n, s):
if n == 1:
return [s]
lx = int(n / 2)
ly = n - lx
lower = max(1 * lx, s - 10 * ly)
upper = min(10 * lx, s - 1 * ly)
b = randint(lower, upper)
result = rand(lx, b)
result.extend(rand(ly, s - b))
return result
Please point out any mistakes I've made!
Edit: Although I doubt it's the case for C#, in some languages we could make this simpler and more efficient by using tail-recursion. First we switch to generating one element at a time:
-- Generate one number then recurse
rand 1 s = [s]
rand n s = let ly = n - 1
lower = max 1 (s - 10 * ly)
upper = min 10 (s - 1 * ly)
x = randInt lower upper
in x : rand (n - 1) s
Then we accumulate the result rather than building up unfinished continuations:
rand' xs 1 s = s:xs
rand' xs n s = let ly = n - 1
lower = max 1 (s - 10 * ly)
upper = min 10 (s - 1 * ly)
x = randInt lower upper
in rand' (x:xs) (n-1) s
rand = rand' []
Upvotes: 0
Reputation: 2523
OK, it could be tricky to do something like that.
If you need to obtain 100 different numbers and you need that they average will be 7, you'll need them to sum 700.
You'll need to keep track of each number and their total sum. While 700 minus the sum of your obtained so far values is lesser than 10 * the amount of numbers you haven't obtained yet, you can continue obtaining pure random values.
When the moment comes that your obtained values sum is lesser than the values you need to obtain, then you change that last number by a 10, put a 10 in the rest of numbers you need to the end of your list and, on the last number, you get the difference between 700 and the sum of your 99 previous pseudo-random values.
Shuffle your array et voilá, you have a 100 pseudo-random array with numbers from 1 to 10 whose average is 7. Surely it will have more 10s than it'll be desired, but sure you'll be able to fine tune this "algorithm" to make it a bit less 10 prone.
Mmmm, wait a moment, what if you get random values that have an average above 7? You'll need to track also that the sum of your current values is lesser than the numbers you have yet to obtain. If you surpass this value in any moment you'll need to convert your last number to a 1, put a 1 on the rest of your needed values and obtain again your last number as the difference between 700 and your 99 earlier values.
Upvotes: 0
Reputation: 79541
A[0], ..., A[99]
to 1
.I = {0, 1, ..., 99}
.i
uniformly from I
.A[i]
.A[i] == 10
, then remove i
from I
.This will guarantee sum(A)
is 700 and thus avg(A)
is 7.
Note however that this does not give a uniform distribution over all such arrays of 100 integers in {1, ..., 10} such that they sum to 700. To devise an algorithm for uniformly sampling would be a much more challenging exercise.
Upvotes: 3
Reputation: 14113
edit: changing the code to always result in an average of exactly 7.
This is basically an optimized version of what you were doing already. Instead of generating another 100 numbers, it generates only 10 before doing the check.
using System.Collections.Generic;
using System.Linq;
var r = new Random();
var numbers = new List<int>();
while (numbers.Count < 100)
{
var stack = new Stack<int>();
for (int i = 0; i < 10; i++)
{
stack.Push(r.Next(10));
}
if (stack.Sum() == 70)
{
numbers.AddRange(stack);
}
}
Console.WriteLine(numbers.Average());
Upvotes: 0
Reputation: 9985
My 2 cents
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Stopwatch watch = Stopwatch.StartNew();
int count = 100;
Double min = 0;
Double max = 10;
Double target = 7;
Double tolerance = 0.00000001;
Double minAverage = target - tolerance;
Double maxAverage = target + tolerance;
Random r = new Random();
List<Double> numbers = new List<double>();
Double sum = 0;
for (int i = 0; i < count; i++)
{
Double d = RangedDouble(min, max, r);
numbers.Add(d);
sum += d;
}
int Adjustments = 0;
while((sum / count < minAverage || (sum / count) > maxAverage))
{
while ((sum / count) < minAverage)
{
Double oldDbl = numbers.First(d => d < minAverage);
Double newDbl = oldDbl + RangedDouble(minAverage - oldDbl, 10 - oldDbl, r);
numbers.Remove(oldDbl);
sum -= oldDbl;
numbers.Add(newDbl);
sum += newDbl;
Adjustments++;
}
while ((sum / count) > maxAverage)
{
Double oldDbl = numbers.First(d => d > maxAverage);
Double newDbl = oldDbl - RangedDouble(oldDbl - maxAverage, oldDbl, r);
numbers.Remove(oldDbl);
sum -= oldDbl;
numbers.Add(newDbl);
sum += newDbl;
Adjustments++;
}
}
watch.Stop();
int x = 0;
while (x < count)
{
Console.WriteLine("{0:F7} {1:F7} {2:F7} {3:F7}", numbers.Skip(x).Take(1).First(), numbers.Skip(x + 1).Take(1).First(), numbers.Skip(x + 2).Take(1).First(), numbers.Skip(x + 3).Take(1).First());
x += 4;
}
Console.WriteLine();
Console.WriteLine(watch.ElapsedMilliseconds);
Console.WriteLine(numbers.Average());
Console.WriteLine(Adjustments);
Console.ReadKey(true);
}
private static double RangedDouble(Double min, Double max, Random r)
{
return (r.NextDouble() * (max - min) + min);
}
}
}
And the output:
8.1510368 7.2103030 7.9909210 9.6693311
8.2275382 7.2839244 8.8634567 7.9751014
7.8643791 7.2262462 9.8914455 9.6875690
8.4396683 8.4308401 7.5380218 8.6147181
8.2760663 7.7399011 7.4312152 9.2115622
9.7850111 9.1061378 9.8672965 9.5610411
7.0415607 8.8446195 9.3562218 8.5279759
7.5227340 9.3572417 9.8927997 9.5880645
9.0908564 7.0918394 9.6213258 8.6528169
9.3803283 9.6869223 1.4006790 3.3310691
7.0719214 2.6370854 9.7558776 8.9180391
3.0486700 5.0082988 8.8624504 5.0497899
0.9692377 7.7140550 9.8495115 6.4933865
4.4939760 9.3187625 5.4353003 6.5405668
9.5693118 5.0339998 6.9644440 4.6902072
0.5241568 9.7748420 0.1406617 8.4731427
9.8064604 6.3113773 0.8628048 9.2417028
8.9148867 9.3111336 3.2424080 9.6710544
4.3794982 5.1687718 9.8207783 0.3283217
9.8321869 2.8093698 7.4377070 4.1130959
5.9840738 9.2560763 3.6691865 2.5498863
7.3242246 7.0179332 5.8906831 9.3340545
0.3735044 7.2442886 0.4409532 9.0749754
9.6716409 8.4097246 2.8069123 7.2970794
2.4964238 8.2826350 9.1115787 3.73739271
6.99992266645471
729
Upvotes: 0
Reputation: 15772
public int RandomNumberThatAveragesToSeven()
{
//Chosen by fair dice roll
//Guaranteed to be random
return 7;
}
Without additional parameters, this above algorithm satisfies each and every requirement.
EDIT Since there was so much controversy on this answer...I added this answer...which is definitely random.
public List<int> ProduceRandom100NumbersWithAverageOfSeven()
{
var rand = new Random();
var seed = rand.Next();
if(seed > 0.5)
{
return new List(Enumerable.Concat(
Enumerable.Repeat(6, 50),
Enumerable.Repeat(8, 50)));
}
else
{
return new List(Enumerable.Concat(
Enumerable.Repeat(8, 50),
Enumerable.Repeat(6, 50)));
}
}
Upvotes: 4
Reputation: 574
Something like this might do it:
public static void Main(string[] args)
{
var randomList = new List<int>();
var random = new Random();
var avg = 0;
while (avg != 7)
{
randomList = new List<int>();
GenerateList(randomList, random);
avg = (int) randomList.Average();
}
for (var i = 0; i < randomList.Count; i++)
{
Console.WriteLine(string.Format("Index: {0}, Number: {1}", i, randomList.ElementAt(i)));
}
}
private static void GenerateList(List<int> refList, Random random)
{
for (var i = 0; i < 100; i++)
{
refList.Add(random.Next(1, 10));
}
}
Upvotes: 0
Reputation: 766
This method generates a random number sequence then keeps adding/subtracting until we get the correct total (700), so long as the number we are altering is still in the range of 1-10
List<int> randomNumbers = new List<int>();
for (int i = 0; i < 100; i++) {
numbers.Add(r.Next(1, 10));
}
int total = randomNumbers.Sum();
// Now fiddle until we get the correct total (700)
if (total < 700) {
while (total < 700) {
for (int i = 0; i < 100; i++) {
if (numbers[i] < 10) {
numbers[i]++;
total = randomNumbers.Sum();
if (total == 700) break;
}
}
}
}
else if (total > 700) {
while (total > 700) {
for (int i = 99; i >= 0; i--) {
if (numbers[i] > 0) {
numbers[i]--;
total = randomNumbers.Sum();
if (total == 700) break;
}
}
}
}
Upvotes: -1