具有固定平均值的随机数

时间:2014-09-10 09:35:45

标签: c# .net random average

我想在1到10之间生成100个随机数。但是这100个随机数的平均值应为7.我怎么能这样做?我这样做:

//generating random number
Random random = new Random();
int value = random.Next(1,10);

将每个值存储在一个数组中。如果数组中100个项目的平均值不是7,那么我需要再获得100个随机数。谁能建议一个更好的方法呢?

10 个答案:

答案 0 :(得分:3)

  1. A[0], ..., A[99]初始化为1
  2. 初始化I = {0, 1, ..., 99}
  3. 重复步骤4-6 600次。
  4. i统一选择随机I
  5. 增量A[i]
  6. 如果A[i] == 10,请从i移除I
  7. 这样可以保证sum(A)为700,因此avg(A)为7。

    但请注意,在{1,...,10}中的100个整数的所有此类数组上提供统一分布,使得它们总和为700为统一采样设计算法将是一项更具挑战性的工作。

答案 1 :(得分:2)

public int RandomNumberThatAveragesToSeven()
{
    //Chosen by fair dice roll
    //Guaranteed to be random
    return 7;
}

如果没有其他参数,上述算法可满足每个要求。

  1. 返回必须介于1到10之间
  2. 多次通话的平均值必须趋于7,因为n倾向于inf。
  3. 编辑由于这个答案引起了很多争议......我添加了这个答案......这绝对是随机的。

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

答案 2 :(得分:1)

此函数用于随机获取n条记录之间的固定平均值。 在我的答案中,“ n”在这里被声明为“ 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);
}

答案 3 :(得分:0)

好吧,做那样的事情可能会很棘手。

如果您需要获得100个不同的数字,并且您需要它们的平均值为7,那么您需要它们总计700个。

您需要跟踪每个数字及其总和。虽然700减去你到目前为止得到的值的总和小于10 *你尚未获得的数字量,但你可以继续获得纯随机值。

当您获得的值总和小于您需要获得的值时,您将最后一个数字改为10,将剩余数字中的10个更改为列表末尾所需的数字,在最后一个数字上,你得到700和你之前的99个伪随机值之和的差值。

随机播放你的阵列etvoilá,你有一个100个伪随机数组,数字从1到10,平均值为7.当然它会比你想要的多10个,但是肯定的你将能够微调这个“算法”,使其不那么容易发生。

嗯,等等,如果得到平均值超过7的随机值怎么办?您还需要跟踪当前值的总和小于您尚未获得的数字。如果你在任何时候超过这个值,你需要将你的最后一个数字转换为1,在你需要的其余值上加1,然后再获得你的最后一个数字作为700和99之前值之间的差值。

答案 4 :(得分:0)

这样的事可能会这样做:

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

答案 5 :(得分:0)

修改:将代码更改为始终平均为7。

这基本上是您已经在做的优化版本。在进行检查之前,它只生成10个而不是生成另外100个数字。

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

答案 6 :(得分:0)

我的2美分

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

输出:

  

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

答案 7 :(得分:0)

与其他答案一样,由于我们知道长度,我们只需关注总和即可获得平均值。

我递归地解决它。在基本情况下,我们需要生成长度为1的列表,该列表总和为某个数字s。这很简单:列表只包含s

rand 1 s = [s]

现在我们可以解决递归情况rand n s,其中n是所需的列表长度,s是所需的总和。为此,我们将生成两个列表xy,并根据给定的约束将它们连接在一起:

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

这些方程/不等式尚未解决,因此我们首先要做的是选择列表的长度。为了降低递归水平,我们可以选择lx = round (n / 2),然后设置以下内容:

length x = lx
length y = n - lx = ly

因此:

sum x + sum y = s
1  * lx <= sum x
10 * lx >= sum x
1  * ly <= sum y
10 * ly >= sum y

我们使用第一个等式来重写不等式:

1  * lx <= sum x
10 * lx >= sum x
1  * ly <= s - sum x
10 * ly >= s - sum x

我们可以重新排列底部两个以使sum x成为主题:

sum x + 1  * ly <= s
sum x + 10 * ly >= s

sum x <= s - 1  * ly
sum x >= s - 10 * ly

我们知道lys所以这些给了我们sum x的明确界限,我们通过获取最大下界和最小上界来组合:

max (1  * lx) (s - 10 * ly) <= sum x
min (10 * lx) (s - 1  * ly) >= sum x

这些界限是有意义的:它们考虑了x中的每个元素都是1或10 的情况,它们确保余数可以由sum y处理。现在我们只在这些边界之间生成一个随机数B,然后设置:

sum x = B
sum y = s - B

由此,我们可以执行递归(假设一些随机数函数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)

现在可以通过调用:

生成列表
myList = rand 100 700

我为了简洁而在Haskell中写了这个,但它只是算术,所以应该很容易转换为C#。如果它有帮助,这是一个Python版本:

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

请指出我所犯的任何错误!

编辑:虽然我对C#的情况有所怀疑,但在某些语言中,我们可以通过使用 tail-recursion 来使这更简单,更高效。首先,我们切换到一次生成一个元素:

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

然后我们累积结果而不是建立未完成的延续:

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' []

答案 8 :(得分: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}");
    }
}

答案 9 :(得分:-1)

此方法生成一个随机数序列,然后继续加/减,直到我们得到正确的总数(700),只要我们改变的数字仍在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;
            }
        }
    }
}