13

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?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
M.S
  • 288
  • 4
  • 12
  • see this question http://stackoverflow.com/questions/17795265/make-random-numbers-tend-average-to-a-specific-value – SomeRandomName Sep 10 '14 at 09:43
  • http://stackoverflow.com/questions/11149051/generate-a-random-number-sequence-to-get-and-average - there is an algorithm that respects the range. –  Sep 10 '14 at 09:45
  • 3
    Does it have to be exactly 7? Because that will only ever happen when all numbers add up to exactly 700. – Steven Liekens Sep 10 '14 at 09:47
  • @Reniuz lol what do you do if the first possibly infinite random numbers wont give you 7 as average. Really bad advice. – SomeRandomName Sep 10 '14 at 09:47
  • 4
    -1 It is not clear what distribution you require, only that it is obviously not a uniform distribution. There are literally infinite ways to create a distribution that fits your requirements. – Aron Sep 10 '14 at 09:55
  • @SomeRandomName The question is a bit unclear. Perhaps the OP wants an expectancy value of 7, or perhaps they want the sum of the 100 numbers to be 7*100. – CodesInChaos Sep 10 '14 at 09:55
  • The problem with this: The numbers won't be as random as they should be. Why do you require an average of 7? – Fabian Bigler Sep 10 '14 at 09:56
  • @Aron As a challenge, generate the distribution with maximal shannon entropy with the given range and expectancy value. – CodesInChaos Sep 10 '14 at 10:08
  • @CodesInChaos Oooo...that's a nice way to describe it. Its pretty simple to solve. First create the space 10P10, then filter for points in the space where the sum = 700, then select a random point...Simples. – Aron Sep 10 '14 at 10:12
  • @Aron notice, that OP expects integer values, as far as I can see. – Ilya Ivanov Sep 10 '14 at 10:13
  • @Aron I was thinking about a distribution with expectancy value 7 and independent rolls, not about sum=700 interpretation. – CodesInChaos Sep 10 '14 at 10:13
  • @CodesInChaos Notice the "Monte Carlo" filter for exactly 700 in his original specification. This would produce the maximal Shannon Entropy, given each point in the phase space would be equally likely. – Aron Sep 10 '14 at 10:16
  • @IlyaIvanov I noted that yes. – Aron Sep 10 '14 at 10:16
  • @Aron you just noted, that the number of solutions is infinite for a sequence of 100 finite integers. p.s. after reading the comments, does this question belong to http://math.stackexchange.com/ ? – Ilya Ivanov Sep 10 '14 at 10:18
  • @IlyaIvanov No I didn't say there were infinite solutions for 100 integers which sum to 700. There are infinite ways to pick the solutions. I do actually believe this is the right place to put this question, as there is an algorithmic aspect to it. At the end of the day all CS is math. – Aron Sep 10 '14 at 10:21
  • @Aron ok, sorry about that. I was just thinking there are infinite ways to print `Hello World`, but again, don't want to be mad. I think some of answers make sense, at least from my perspective. – Ilya Ivanov Sep 10 '14 at 10:25
  • What distribution do you want? – David Heffernan Sep 10 '14 at 10:50
  • I am sorry but i am not that clear about distribution. As per my requirement , the average of 100 numbers should be 7. If not strictly, but yeah almost 7.I just wanted the best way to do. @David can you suggest the best distribution. – M.S Sep 10 '14 at 12:38
  • 1
    No. That's your job. Only you know the answer. – David Heffernan Sep 10 '14 at 12:40
  • @Reniuz - that is utterly wrong. You should delete the comment as it will add mass confusion. – Fattie Sep 10 '14 at 14:50
  • @M.S I've already given you two algorithms that give two "best" distributions. In both cases they are the best by the metric of "simple". Name your metric we can MAYBE help you. – Aron Sep 10 '14 at 15:03
  • "the number of solutions is infinite for a sequence of 100 finite integers" total nonsense, of course not dude. – Fattie Sep 10 '14 at 16:04
  • @Aron Thanks!! But that only will have 6 and 8. May be my question was unclear but i need to have all numbers. No strict rules how many number each but it shouldn't be any fixed number – M.S Sep 12 '14 at 06:57
  • 1
    @M.S That is my POINT. Your question leaves too much wiggle room. I have absolutely no idea what you want. Since its clear you don't either, it is perhaps much more useful if you ask on Math, what random numbers you want, giving them HOW you want to use the numbers. You haven't given us how the numbers are consumed...and that is very important to determine the distribution. – Aron Sep 12 '14 at 07:03
  • @Aron Sure!will take help of Math. Added If it make sense to my question. The average should be 7 which might have more 6, 7, 8 (but this shouldn't be the fixed numbers) but should contain all the numbers. – M.S Sep 12 '14 at 07:12

10 Answers10

4
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.

  1. Return must be between 1 and 10
  2. Average of multiple calls must tend to 7 as n tends to inf.

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)));

    }
}
Aron
  • 15,464
  • 3
  • 31
  • 64
  • 3
    The key requirement 'Random Number' is not fulfilled, though. :) – Fabian Bigler Sep 10 '14 at 09:59
  • It even fulfills the stronger requirement that the average of 100 numbers is exactly 7. – CodesInChaos Sep 10 '14 at 09:59
  • 1
    @fabigler Yes it is. Firstly the OP did not specify "how random". Secondly I know of no implementation on known hardware where the result is guaranteed to be 7. – Aron Sep 10 '14 at 10:00
  • do you always need a specification on "how random" to produce a desired result? While the joke is funny, I don't get why this question can't be answered. – Ilya Ivanov Sep 10 '14 at 10:02
  • @IlyaIvanov I am completely serious. The OP removed one of the key assumptions of all well known random number generators, which is fair distribution. That needs to be replaced. My answer was to highlight that the OP did not give enough information in his specification. – Aron Sep 10 '14 at 10:05
  • @StevenLiekens Most dice have more than 7 sides. DIE is the singular of dice. Ever played Monopoly? – Aron Sep 10 '14 at 10:06
  • 3
    It's the answer an underdefined question like this deserves. Admittedly it's the most degenerate corner case, but it's a valid distribution with EV=7. – CodesInChaos Sep 10 '14 at 10:07
  • 2
    @CodesInChaos do we have to answer all incomplete questions like this? I imagine what would SO transform to – Ilya Ivanov Sep 10 '14 at 10:11
  • 2
    @IlyaIvanov I'm just a bit grumpy since randomness related questions rarely specify the desired distribution, which means that are infinitely many, equally valid answers. – CodesInChaos Sep 10 '14 at 10:20
  • +1 for making a good point with additional code. I guess OP wanted just uniform distribution and it would be nice if you added a description on why this sequence can't be uniformly distributed, as you noted in comments for question. – Ilya Ivanov Sep 10 '14 at 11:40
  • @IlyaIvanov If he wanted a uniform distribution, it's not very likely to average 7, is it? – Mark Pattison Sep 10 '14 at 14:55
  • @StevenLiekens I guess you have never played D&D and the like, it uses all sorts of [non cubic dice](http://en.wikipedia.org/wiki/Dice#Non-cubic). – Scott Chamberlain Sep 10 '14 at 14:56
  • 1
    @JoeBlow If Common English has clear and obvious meanings then we wouldn't have invented an entire, nay LOTS of different languages for mathematics. Firstly bad distributions have been the fall of an assheap of crypto-schemes, the most recent in my memory being Sony PS3. Secondly without an understanding of the meaning of the result it is meaningless to say random. For example, if I told you to give me two "random" numbers between 0-360 and 0-180, you might not give me a good distribution for theta and phi. – Aron Sep 10 '14 at 16:23
  • @JoeBlow In COMMON ENGLISH, "Random" means fair distribution. BUT his question also precluded a fair distribution. Its like he said, "Draw me an Elephant, but not grey", I draw a purple one, and you moan its obviously not what he meant... – Aron Sep 10 '14 at 16:28
  • @JoeBlow I first lodged a request for more information. Without any, I proceeded with the most basic result. If I told someone to do something, and they asked IMMEDIATELY for a clarification, I wouldn't expect them to do any real work until clarification was given. Given that the OP did not seem to understand the importance of a distribution, I gave him this. It was meant as learning tool, to highlight what a difference a distribution makes. – Aron Sep 10 '14 at 16:42
3
  1. Initialize A[0], ..., A[99] to 1.
  2. Initialize I = {0, 1, ..., 99}.
  3. Repeat steps 4-6 600 times.
  4. Pick random i uniformly from I.
  5. Increment A[i].
  6. If 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.

Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
0

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.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Bardo
  • 2,470
  • 2
  • 24
  • 42
0

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));
        }
    }
Stephen Walker
  • 574
  • 3
  • 10
0

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());
Steven Liekens
  • 13,266
  • 8
  • 59
  • 85
0

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.7373927

1
6.99992266645471
729

James
  • 9,774
  • 5
  • 34
  • 58
  • @JoeBlow yep, should've spotted that really, would have made it simpler, not that it'd be difficult to cast the result set – James Sep 10 '14 at 16:15
0

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' []
Warbo
  • 2,611
  • 1
  • 29
  • 23
0

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);
}
Amin Golmahalleh
  • 3,585
  • 2
  • 23
  • 36
0
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}");
    }
}
Gurgen Hakobyan
  • 951
  • 10
  • 13
-1

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;
            }
        }
    }
}
SimonPJ
  • 756
  • 9
  • 21