【问题标题】:Random number with fixed average具有固定平均值的随机数
【发布时间】:2014-11-03 21:36:38
【问题描述】:

我想生成 100 个介于 1 和 10 之间的随机数。但是这 100 个随机数的平均值应该是 7。我该怎么做?我这样做如下:

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

并将每个值存储在一个数组中。如果数组中 100 个项目的平均值不是 7,那么我需要再获得 100 个随机数。任何人都可以提出更好的方法吗?

【问题讨论】:

  • 必须是 7 吗?因为只有当所有数字加起来正好是 700 时才会发生这种情况。
  • -1 不清楚您需要什么分布,只是它显然不是均匀分布。有无数种方法可以创建符合您要求的发行版。
  • 没有。那是你的工作。只有你知道答案。
  • @M.S 这是我的观点。你的问题留下了太多的回旋余地。我完全不知道你想要什么。因为很明显你也不知道,如果你问数学,你想要什么随机数,告诉他们你想如何使用这些数字,它可能会更有用。你还没有告诉我们这些数字是如何被消耗的……这对于确定分布非常重要。

标签: c# .net random average


【解决方案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;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

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

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

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

    当您获得的值总和小于您需要获得的值时,您将最后一个数字更改 10,将 10 放在您需要的其余数字中,然后,在最后一个数字上,您会得到 700 与之前 99 个伪随机值之和之间的差。

    随机排列你的数组etvoilá,你有一个 100 个伪随机数组,其中的数字从 1 到 10,平均值为 7。当然它的 10 比预期的要多,但可以肯定您将能够微调这个“算法”,使其不那么容易发生 10 次。

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

    【讨论】:

      【解决方案3】:

      这样的事情可能会做到:

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

      【讨论】:

        【解决方案4】:
        public int RandomNumberThatAveragesToSeven()
        {
            //Chosen by fair dice roll
            //Guaranteed to be random
            return 7;
        }
        

        没有额外的参数,上面的算法满足每一个要求。

        1. 返回值必须在 1 到 10 之间
        2. 多次调用的平均值必须趋于 7,因为 n 趋于 inf。

        编辑由于这个答案有很多争议......我添加了这个答案......这绝对是随机的。

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

        【讨论】:

        • 但是,关键要求“随机数”没有得到满足。 :)
        • @fabigler 是的。首先,OP没有指定“随机性”。其次,我知道在已知硬件上没有实现,结果保证为 7。
        • 这是一个像这样定义不足的问题应得的答案。诚然,这是最退化的极端情况,但它是 EV=7 的有效分布。
        • @CodesInChaos 我们是否必须像这样回答所有不完整的问题?我想 SO 会变成什么
        • @IlyaIvanov 我有点脾气暴躁,因为与随机性相关的问题很少指定所需的分布,这意味着有无限多个同样有效的答案。
        【解决方案5】:

        编辑:将代码更改为始终导致平均值恰好为 7。

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

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

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

          【讨论】:

          • @JoeBlow 是的,应该已经发现,真的,会更简单,而不是很难转换结果集
          【解决方案7】:
          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

          这将保证sum(A) 是700,因此avg(A) 是7。

          但是请注意,这 not 不会在 {1, ..., 10} 中包含 100 个整数的所有此类数组上给出 均匀 分布,因此它们的总和为 700 . 为均匀采样设计一种算法将是一项更具挑战性的工作。

          【讨论】:

          • 这将给出一个特定的分布,它可能适合也可能不适合 OP 的需求。
          • @MarkPattison 这就是我写最后一段的原因。
          【解决方案8】:

          就像发布的其他答案一样,因为我们知道长度,我们可以通过只关注总和来获得平均值。

          我会递归地解决它。在基本情况下,我们需要生成一个长度为 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' []
          

          【讨论】:

            【解决方案9】:

            此功能用于随机获取 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);
            }
            

            【讨论】:

              【解决方案10】:
              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}");
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 2015-07-06
                • 2013-09-25
                • 2013-03-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多