范围A和B之间的随机数,总计为特定数字

时间:2019-06-25 11:40:25

标签: unity3d game-development

我正在寻找生成一些随机数字,这些数字可以累加为一个特定数字。

示例:

范围(10,35)

总和:100

要生成的数字:5

最好的方法是什么?试图写我自己的逻辑,但作为一个伪代码看起来很混乱。我可以遵循的任何算法?看到类似的问题,但没有得到适当的答案。

5 个答案:

答案 0 :(得分:1)

一个想法可能是考虑以下事实:前4个数字的随机性将有助于最后一个数字的随机性。遵循您的示例:

  1. 100/5 =20。生成的平均数不能大于20。每次生成的数字大于20时,都可以将差值减去下一个生成的值。
  2. a = rand(10,35)。假设a =20。我们很好。
  3. b = rand(10,35)。假设b =35。我们有15个delta。
  4. c = rand(10,35-15)= rand(10,20)= 18。
  5. d = rand(10,20)= 12。
  6. e = 100-(a + b + c + d)= 15

您现在有5个随机数,总计为100。

注意:@derHugo的话非常好。增量调整可双向进行。如果第一个数字是10,则下一个数字将是rand(10 + 10,35)= rand(20,35)。等等

答案 1 :(得分:0)

您只需使用System.Random来生成范围之间的随机数,然后在检查总和是否等于结果时继续将它们相加。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RandomNumberGenerationWithSum : MonoBehaviour
{

    string output = "";
    int sum = 0;
    int result = 100;    //Give the sum

    void Start()
    {
        System.Random r = new System.Random();

        while (sum != result)
        {
            int add;
            if ((result - sum) > 35) //maximum limit
            {
                add = r.Next(10, 35); //define the range
            }
            else
            {
                add = r.Next(result - sum + 1);
            }

            sum += add;
            output += add.ToString() + " + ";
        }

        output = output.Remove(output.Length - 2);

        Debug.Log("Output: " + output);
    }

}

答案 2 :(得分:0)

在这里,我更新了一个使用数组创建的新脚本。因此,您可以在总体中增加大小,例如5。因此,将仅生成5个随机数以将值相加以获得结果。希望这会有所帮助!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RandomInitial : MonoBehaviour
{

    public int[] population;
    int result = 100;    //Give the sum
    int z;
    private bool Check = true;

    void Start()
    {


    }

    void Update()
    {
        if (Check == true)
        {

            for (int i = 0; i < population.Length; i++)
            {
                population[i] = Random.Range(10, 35);
            }

            for (int i = 0; i < population.Length; i++)
            {
                z += population[i];
            }
            Debug.Log("value " + z);
        }

        if (z == result)
        {
            Debug.Log("Success");
            Check = false;
        }
        else
        if (z != result)
        {
            z = 0;
        }
    }

}

答案 3 :(得分:0)

好吧。所以最简单的方法是: 只要做一个循环,从目标数中减去随机数即可。 重复执行n-1次,然后检查剩下的那一个是否在范围内。 我将用php编写代码,而不是将其发布在这里

编辑:在这里

注意:它确实有效,但是它不是没有错误,您必须添加一些防御性的东西,因此它不会超过$ targetNum

<?php
    $targetNum = 100;
    $randMin =10;
    $randMax = 35;
    $generateNum = 5;

    $numArray = array();

    $numLeft=$targetNum;

    for($i = 0; $i<$generateNum-1;$i++){
        $numArray[$i] = rand($randMin,$randMax);
        $numLeft -= $numArray[$i];
        if($i==$generateNum-2){
            $numArray[$i+1] = $numLeft;
        }
    }
    for($i = 0; $i<$generateNum;$i++){
        echo $numArray[$i];
        echo ", ";
    }
?>

答案 4 :(得分:0)

我遵循Zedka29的回答编写了此JavaScript代码,但是在选择不同数量的数字来生成时遇到了一些错误。在您的示例中,如果最大范围是20,则最终会留下一个缺口,因为在这种情况下,第一个数字可能会小于20。

    function genRand(min, max, target, numItems) {
        let total = 0;  
        let maxNum = Math.floor(target/min);
        let minNum = Math.ceil(target/max);
        
        if (!numItems) numItems = Math.floor(Math.random() * (maxNum - minNum) + minNum);
        
        let avg = Math.round(target / numItems);
        let tries = 0;
        while (total < target && tries < maxNum) { 
            let tmp = Math.floor(Math.random() * (max - min) + min); 
            if (total + tmp > target) {
                tmp = target - total;
            }
            else if (total + tmp > target - min) {
                tmp += target - (total + tmp);
                if (tmp > max) {
                    // SOMETHING WRONG
                }
            }
            else {
                if (tmp < avg) { 
                    min += (avg - tmp); 
                } 
                else if (tmp > avg) { 
                    max -= (tmp - avg); 
                } 
            }
            total += tmp; 
            

            console.log(tmp + ', new min: ' + min + ', new max: ' + max); 
            console.log('total: ' + total);
            ++tries;
        } 
        console.log('numItems ' + numItems);
        console.log('nums generated: ' + tries);
    }

    genRand(10,20,100, 5);

但是,在本文中运行了几次之后,我意识到最后一个数字似乎总是30。10,最小值,大于最大值...我想知道该信息是否有用。

编辑1

在努力弄清Zedka29的答案出了什么问题之后,我使用递归和数组为这个问题开发了一种蛮力的解决方案。下面的代码将生成范围内所有可能的,唯一的,经过排序的数字组合,这些数字加起来即为总数。要实际生成所需的随机数,请对当前的数字数组进行排序,并检查其是否在此处所述的蛮力方法生成的数组中。注意:此方法使用了不必要的内存,仍然可以对其进行优化,但是无论用户输入了什么,都会避免剩余数太大或太小。

以下是指向repl.it的链接:https://repl.it/@geraldgehrke/RandomLengthsTesting-1

(function(){

  const total = 41; 
  const size = 5; 
  let arr = [];
  let cntrs = new Array(size);
  const min = 5;
  const max = 10;
  let maxNum = Math.floor(total/min);
  let minNum = Math.ceil(total/max);
  if (minNum > maxNum) {
    minNum = maxNum;
  }

  for (let i = minNum; i <= maxNum; ++i) {
    cntrs = new Array(i);
    arr = [];
    generateSums(total, i, arr, cntrs, min, max);
  }

  function generateSums(total, size, arr, cntrs, min, max) {
    function count(index) {
      ++cntrs[index];
      let sum = 0;
      for (let i of cntrs) {
        sum += i;
      }
      if (sum == total) {
        arr.push(cntrs.slice(0));
      }
      if (index < size - 1) {
        count(index + 1);
      }
    }
    function loop(prevIndex, loopLevel, maxLoopLevel) {
      if (loopLevel < maxLoopLevel) {
        for (let i = prevIndex; i < size; ++i) {
          loop(i, loopLevel + 1, maxLoopLevel);
        }
      }
      else {
        for (let i = prevIndex; i < size; ++i) {
          cntrs.fill(min, i+1);
          count(i);
        }
      }
    }
    cntrs.fill(min, 0);
    let sum = 0;
    for (let i of cntrs) {
      sum += i;
    }
    if (sum == total) {
      arr.push(cntrs.slice(0));
    }
    count(0);
    for (let i = 1; i < max-min; ++i) {
      loop(0,1,i);
    }
    console.log(arr);
  }

})();