Get `n` random values between 2 numbers having average `x`

前端 未结 5 633
你的背包
你的背包 2021-01-28 12:21

I want to get n random numbers(e.g n=16)(whole numbers) between 1 to 5(including both) so that average is x.

x can be any value between (1, 1.5, 2, 2.5, 3,

相关标签:
5条回答
  • 2021-01-28 12:32

    If I got it right, I would suggest you to have the average, than generate a number below the average and then to add a number with the same distance from average to the other side. For example, average 4 has the maximum border distance of 1 from nearest limit 5, so you should generate between 3, 4, 5. If 3 is generated - put 5 next. If 5, then 3. If 4 - put 4 next. And so on 8 times.

    The best way to solve it is to put it this way:

    average = sum of all numbers / amount of numbers, therefor, average * amount = sum, as Michael said. Now, if your sum is not integer - you will have no solution for this one.

    It means that no matter, which method to use - mine, or Michael's. The difference is Michael's method doubles randomness with larger execution time.

    0 讨论(0)
  • 2021-01-28 12:40

    I would implement it like this:

    1. Choose n random numbers
    2. Calculate average
    3. Randomly choose one of the n random numbers
    4. Add or subtract 1 from the number, depending on whether the current average is above or below x
    5. Repeat from step 2 until the current average is x (or close enough)
    0 讨论(0)
  • 2021-01-28 12:42

    Edit: I have re-written this to avoid having to call the function recursively.

    <?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());
    
    0 讨论(0)
  • 2021-01-28 12:47

    This answer allows any value for the target average (regardless of whether n is odd or even) and avoids the use of recursion to optimize performance.

    The Function

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


    Example Output:

    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
    
    0 讨论(0)
  • 2021-01-28 12:49

    I have written the JS implementation for this:

    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)

    0 讨论(0)
提交回复
热议问题