【问题标题】:Get `n` random values between 2 numbers having average `x`在具有平均“x”的2个数字之间获取“n”随机值
【发布时间】:2012-04-14 04:14:25
【问题描述】:

我想得到n 1 到 5(包括两者)之间的随机数(例如 n=16)(整数),因此平均值为 x。

x 可以是 (1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5) 之间的任何值。

我正在使用 PHP。

例如 假设我的平均 x=3。

然后要求 1 到 5 之间的 16 个整数(包括两者)。 比如 (1,5,3,3,3,3,2,4,2,4,1,5,1,5,3,3)

更新:

如果 x=3.5 表示 16 个数字的平均值应该在 3.5 到 4 之间。
如果 x=4 表示 16 个数字的平均值应该在 4 到 4.5 之间
如果 x=5 表示所有数字都是 5

【问题讨论】:

  • 你能展示一些你也想过的想法吗??
  • 真正的平均值只能是3,如果数字是真正随机的......
  • 添加数字需要达到指定平均值的约束可以消除随机性。例如,在您的示例中,您可以从该组中选择 8 个随机数,然后为每个数字找到两者的平均值为 3 的另一个数字。这应该可行,但是您不再有所有 16 个随机值,而您永远不会。
  • 我已经更新了问题。
  • 那么到目前为止,您实际尝试过什么?

标签: php algorithm random range average


【解决方案1】:

此答案允许目标平均值的任何值(无论 n 是奇数还是偶数),并避免使用递归来优化性能。

函数

function getRandomNumbersWithAverage($target_average, $n, $min=1, $max=5)
{

  if($min>$max) list($min, $max) = array($max, $min);
  if($target_average<$min || $target_average>$max) return false;
  else if($target_average==$min) return array_fill(0, $n, $min);
  else if($target_average==$max) return array_fill(0, $n, $max);

  if($n<1) return false;
  else if($n==1) return array($target_average);
  else
  {
    $numbers = array();
    for($i=0;$i<$n;$i++)
    {
      $sum = array_sum($numbers);
      $average = $i ? $sum/($i+1) : ($min+$max)/2;
      $contrived_number = $target_average*($i+1) - $sum;
      // Last one must be contrived
      if($i==$n-1) $new_number = ceil($contrived_number); // Round up
      else
      {
        // The tolerance gets smaller with iteration
        $tolerance = ($max-$min)*(1-($i/($n-1)));
        $temp_min = ($contrived_number-$tolerance);
        if($temp_min<$min) $temp_min = $min;
        $temp_max = ($contrived_number+$tolerance);
        if($temp_max>$max) $temp_max = $max;
        $new_number = mt_rand($temp_min, $temp_max);
      }
      if($new_number==0) $new_number = 0; // Handle -0
      $numbers[] = $new_number;
    }
    // Since the numbers get more contrived towards the end, it might be nice to shuffle
    shuffle($numbers);
    return $numbers;
  }
}


示例输出:

getRandomNumbersWithAverage(1, 12)

produced the numbers: (1,1,1,1,1,1,1,1,1,1,1,1) having an average of: 1


getRandomNumbersWithAverage(1.1, 13)

produced the numbers: (1,1,1,1,1,1,1,4,1,1,1,0,1) having an average of: 1.1538461538462


getRandomNumbersWithAverage(2.7, 14)

produced the numbers: (3,3,2,5,1,2,4,3,3,2,3,3,3,1) having an average of: 2.7142857142857


getRandomNumbersWithAverage(2.7, 15)

produced the numbers: (3,3,4,3,4,2,1,1,3,2,4,1,5,1,4) having an average of: 2.7333333333333


getRandomNumbersWithAverage(3.5, 16)

produced the numbers: (5,5,4,3,1,5,5,1,2,5,3,3,4,4,4,2) having an average of: 3.5


getRandomNumbersWithAverage(3.5, 17)

produced the numbers: (5,2,3,5,4,1,2,3,5,4,5,4,2,3,5,3,4) having an average of: 3.5294117647059


getRandomNumbersWithAverage(4, 18)

produced the numbers: (3,5,5,3,5,5,3,4,4,4,5,2,5,1,5,4,5,4) having an average of: 4


getRandomNumbersWithAverage(4.9, 19)

produced the numbers: (5,5,5,5,7,5,5,5,5,6,5,3,5,5,3,5,5,5,5) having an average of: 4.9473684210526


getRandomNumbersWithAverage(5, 20)

produced the numbers: (5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5) having an average of: 5


getRandomNumbersWithAverage(0.5, 10)

does not produce numbers


getRandomNumbersWithAverage(0, 9)

does not produce numbers


getRandomNumbersWithAverage(-1, 8)

does not produce numbers


getRandomNumbersWithAverage(5.5, 7)

does not produce numbers


getRandomNumbersWithAverage(6, 6)

does not produce numbers


getRandomNumbersWithAverage(6, 5, 1, 7)

produced the numbers: (7,7,2,7,7) having an average of: 6


getRandomNumbersWithAverage(6, 5, 1, 6)

produced the numbers: (6,6,6,6,6) having an average of: 6


getRandomNumbersWithAverage(3, 1)

produced the numbers: (3) having an average of: 3

【讨论】:

  • 它对我来说也很好,我只是编辑了一点点来生成随机数而不是整数,但我发现这里@9​​87654323@ 是冗余,否则工作正常:) –
【解决方案2】:

编辑:我已重写此代码以避免递归调用该函数。

<?php

  /**
   * Get an array of random numbers between the given range with a given average value
   *
   * @param integer $min
   * @param integer $max
   * @param integer $count
   * @param integer|float $average
   * @return boolean|array
   */
  function getRandomNumbers($min = 1, $max = 5, $count = 16, $average = 3)
  {

    // Return FALSE if the range and/or the count are not all integers
    if (!is_int($min) || !is_int($max) || !is_int($count))
    {
      return FALSE;
    }

    // Round the average if the target total would be impossible
    if (!is_int($count * $average))
    {
      $average = round($average);
    }

    // Get the target total
    $total = $count * $average;

    // Return FALSE is the result is impossible
    if ($min > $max || $min * $count > $total || $max * $count < $total)
    {
      return FALSE;
    }

    // Get the specified number of random integers
    for ($i = 0; $i < $count; ++$i)
    {

      // Get a random number within the given range
      $rand = mt_rand($min, $max);

      // As a default do not continue
      $cont = FALSE;

      // Check to see if the random number is acceptable and if not change it until it is
      while (!$cont)
      {

        // If the number is too high then decrease it by one
        if (($total - $rand) - (($count - 1 - $i) * $min) < 0)
        {
          --$rand;
        }

        // Otherwise if the number is too low then increase it by one
        elseif (($total - $rand) - (($count - 1 - $i) * $max) > 0)
        {
          ++$rand;
        }

        // Otherwise we can continue
        else
        {
          $cont = TRUE;
        }

      }

      // Store the number and minus it from the total
      $total -= $result[] = $rand;

    }

    // Return the result
    return $result;

  }

  // Output an array of random numbers
  print_r(getRandomNumbers());

【讨论】:

  • 这种递归会很危险。对不起,伙计,但你的建议是大海捞针......尤其是当你有一个浮动平均值时。对于大多数情况,这将没有解决方案。
  • @MichaelSazonov,是的;我确实说过,只有在你知道有解决方案的情况下它才会起作用。
  • @SomnathMuluk,你有什么错误吗?结果不正确?
  • 现在得到结果。没有得到 1,5,4.5。所以平均 1,5 意味着所有没有。是一样的。只有 4.5 的结果应该得到。
  • 在您的问题中:“如果 x=3.5 表示 16 个数字的平均值应该在 3.5 到 4 之间。”仅当x 不是整数且n 为奇数时才会发生这种情况。例如,如果x = 3.5 和n = 17,那么这 17 个整数需要加起来为 59.5,这是不可能的。所以平均值必须是 4。
【解决方案3】:

我会这样实现它:

  1. 选择n个随机数
  2. 计算平均值
  3. 从n个随机数中随机选择一个
  4. 从数字中加或减 1,具体取决于当前平均值是高于还是低于 x
  5. 从第 2 步开始重复,直到当前平均值为 x(或足够接近)

【讨论】:

    【解决方案4】:

    我已经为此编写了 JS 实现:

    function getRandom(min, max) {
      return Math.random() * (max - min) + min;
    }
    function getArr(size, avg, min, max) {
      let arr = [];
      let tmax = max;
      let tmin = min;
      while (arr.length < size) {
        const variable1 = +getRandom(min, tmax).toFixed(1);
        let variable2 = +(avg * 2 - variable1).toFixed(1);
        if (variable2 < min) {
          tmax = max - (min - variable2);
          variable2 = min;
        } else if (variable2 > max) {
          tmin = min + (variable2 - max);
          variable2 = max;
        } else {
          tmax = max;
          tmin = min;
        }
        arr = arr.concat([variable1, variable2]);
      }
      let sumErr = arr.reduce((a, b) => a + b, 0) - avg * size;
      if (sumErr > 0) {
        arr = arr.map((x) => {
          if (x > min && sumErr > 0.001) {
            let maxReduce = x - min;
            if (maxReduce > sumErr) {
              const toReturn = +(x - sumErr).toFixed(1);
              sumErr = 0;
              return toReturn;
            } else {
              sumErr -= maxReduce;
              return min;
            }
          }
          return x;
        });
      } else {
        arr = arr.map((x) => {
          if (x < max && sumErr < -0.001) {
            let maxAdd = max - x;
            if (maxAdd > Math.abs(sumErr)) {
              const toReturn = +(x + Math.abs(sumErr)).toFixed(1);
              sumErr = 0;
              return toReturn;
            } else {
              sumErr += maxAdd;
              return max;
            }
          }
          return x;
        });
      }
    
      return arr.sort(() => Math.random() - 0.5);
    }
    
    const output = getArr(40, 2.01, 0.5, 2.5)
    const outputAvg = output.reduce((a, b) => a + b, 0) / 40
    console.log(`givenAvg: ${2.01} outputAvg: ${outputAvg}`)
    console.log(output)

    【讨论】:

      【解决方案5】:

      如果我做对了,我建议你取平均值,而不是生成一个低于平均值的数字,然后添加一个从平均值到另一边的距离相同的数字。例如,平均 4 与最近限制 5 的最大边界距离为 1,因此您应该在 3、4、5 之间生成。如果生成了 3 - 接下来放置 5。如果是 5,那么 3。如果是 4 - 接下来放 4。以此类推8次。

      解决它的最好方法是这样说:

      平均=所有数字的总和/数字的数量,因此, 正如迈克尔所说,平均 * 金额 = 总和。现在,如果你的总和不是整数 - 你将无法解决这个问题。

      这意味着无论使用哪种方法 - 我的或迈克尔的。不同之处在于 Michael 的方法将随机性加倍,执行时间更长。

      【讨论】:

      • 但我可以平均 3.5、1.5。我需要随机数作为整数。
      • 仍然有效 - 如果您的平均分数为 1.5,那么您需要 1 和 2,3.5 将需要 3 和 4 或 2 和 5、1 和 6,等等。@Michael's建议仍然有效。
      • 您可以为此构建一个算法,但不会有随机性。
      • @TheOx:我可以取 (1,2,3,4,5) 而不是 6 之间的数字。
      • 好的,所以忽略“1和6”部分。
      猜你喜欢
      • 1970-01-01
      • 2015-10-25
      • 2014-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-09
      • 2015-01-22
      • 1970-01-01
      相关资源
      最近更新 更多