将数字拆分为4个随机数

时间:2018-05-18 06:51:32

标签: javascript

我想将10拆分为4个随机数的数组,但它们都不能0或高于4。例如[1,2,3,4][1,4,4,1][4,2,3,1]

我认为这是一个简单的问题,但由于某种原因,我无法想到如何做到这一点。如果某人有一些非常有帮助的指示!

编辑: 这是我现在的代码,但我也生成了一个10以下的总数:

  let formation = [];
  let total = 0;

   for (let i = 0; i < 4; i ++) {
    if (total < 9) {
      formation[i] = Math.floor(Math.random() * 4) + 1; 
    } else {
      formation[i] = 1;
    }
  }

7 个答案:

答案 0 :(得分:26)

您可以创建所有可能的组合并选择随机数组。

function get4() {

    function iter(temp) {
        return function (v) {
            var t = temp.concat(v);
            if (t.length === 4) {
                if (t.reduce(add) === 10) {
                    result.push(t);
                }
                return;
            }
            values.forEach(iter(t));
        };
    }
    
    const
        add = (a, b) => a + b,
        values = [1, 2, 3, 4],
        result = [];

    values.forEach(iter([]));
    return result;
}

console.log(get4().map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

用于获取没有所有可能组合列表的随机值的算法

它的工作原理是使用随机值和偏移量的因子,基于实际总和,指数,下一个索引所需的最小总和以及最大总和。

偏移通常是最小总和,或者总和与最大总和之差的较大值。为获得该因子,将三个值作为乘以随机值的最小值。

  

该表根据给定值和获取所有值的迭代,说明总和和所需迭代的所有可能值。

     

在开始时,总和是小部分分配的值。结果是第二个块的余额为14 ... 10,因为可以取1 ... 5的值。第三轮遵循相同的规则。最后,剩余的总和被视为该值的偏移量。

1,...,5值和5元素的总和为15以及所有可能性的示例:

min:     1
max:     5
length:  5
sum:    15

smin = (length - index - 1) * min
smax = (length - index - 1) * max
offset = Math.max(sum - smax, min)
random = 1 + Math.min(sum - offset, max - offset, sum - smin - min)

    index     sum    sum min  sum max   random   offset
  -------  -------  -------  -------  -------  -------
_      0       15        4       20        5        1
       1       14        3       15        5        1
       1       13        3       15        5        1
       1       12        3       15        5        1
       1       11        3       15        5        1
_      1       10        3       15        5        1
       2       13        2       10        3        3
       2       12        2       10        4        2
       2       11        2       10        5        1
       2       10        2       10        5        1
       2        9        2       10        5        1
       2        8        2       10        5        1
       2        7        2       10        5        1
       2        6        2       10        4        1
_      2        5        2       10        3        1
       3       10        1        5        1        5
       3        9        1        5        2        4
       3        8        1        5        3        3
       3        7        1        5        4        2
       3        6        1        5        5        1
       3        5        1        5        4        1
       3        4        1        5        3        1
       3        3        1        5        2        1
_      3        2        1        5        1        1
       4        5        0        0        1        5
       4        4        0        0        1        4
       4        3        0        0        1        3
       4        2        0        0        1        2
       4        1        0        0        1        1

示例代码采用目标1,...,4,其长度为4个部分,总和为10

function getRandom(min, max, length, sum) {
    return Array.from(
        { length },
        (_, i) => {
            var smin = (length - i - 1) * min,
                smax = (length - i - 1) * max,
                offset = Math.max(sum - smax, min),
                random = 1 + Math.min(sum - offset, max - offset, sum - smin - min),
                value = Math.floor(Math.random() * random + offset);

            sum -= value;
            return value;
        }
    );
}

console.log(Array.from({ length: 10 }, _ => getRandom(1, 4, 4, 10).join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:2)

最简单的解决方案是蛮力。

  1. 设置while循环以将计算嵌套在
  2. 在循环中,创建一个空数组并用随机值填充它,直到达到长度
  3. 检查数组的总和是否是您想要的值,如果是,则打破循环
  4. 以上情况应该一直持续到你有结果为止。

    但值得考虑的两件事情。

    1. 您可以通过计算,数组长度最小值不超过总和来轻松测试解决方案是否完全可行em>数组长度次最大值不小于总和。
    2. 基于随机条件的循环可能永远运行,因此可能需要最大量的迭代。
    3. 下面的代码段中都考虑了这两点:

      function randomNumber(max, min) {
        while (true) {
          var r = Math.round(Math.random() * max);
          if (r >= min) {
            return r;
          }
        }
      }
      
      function splitXintoYComponentsBetweenMaxAndMin(numberToSplit, numberOfSplits, maxValue, minValue, onUpdate) {
        if (minValue === void 0) {
          minValue = 1;
        }
        //Test that a result can exist
        if (maxValue * numberOfSplits < numberToSplit || minValue * numberOfSplits > numberToSplit) {
          return new Promise(function(resolve, reject) {
            resolve(false);
          });
        }
        //Create returner array
        var arr = [];
        var accumulator = 0;
        while (arr.length < numberOfSplits) {
          var val = randomNumber(Math.floor(numberToSplit / numberOfSplits), minValue);
          accumulator += val;
          arr.push(val);
        }
        return new Promise(function(resolve, reject) {
          function runTest() {
            var d = Date.now();
            var localMaxValue = Math.min(maxValue, Math.ceil((numberToSplit - accumulator) / 4));
            //Combination loop
            while (accumulator < numberToSplit && Date.now() - d < 17) {
              var index = Math.round(Math.random() * (arr.length - 1));
              if (arr[index] >= maxValue) {
                continue;
              }
              var r = randomNumber(localMaxValue, minValue);
              while (arr[index] + r > maxValue || accumulator + r > numberToSplit) {
                if (Date.now() - d >= 17) {
                  break;
                }
                r = randomNumber(localMaxValue, minValue);
              }
              if (arr[index] + r > maxValue || accumulator + r > numberToSplit) {
                continue;
              }
              arr[index] += r;
              accumulator += r;
            }
            if (accumulator < numberToSplit) {
              if (onUpdate !== void 0) {
                onUpdate(arr);
              }
              requestAnimationFrame(runTest);
            } else {
              resolve(arr);
            }
          }
          runTest();
        });
      }
      //TEST
      var table = document.body.appendChild(document.createElement('table'));
      table.innerHTML = "<thead><tr><th>Number to split</th><th>Number of splits</th><th>Max value</th><th>Min value</th><th>Run</th></tr></thead>" +
        "<tbody><tr><th><input id=\"number-to-split\" value=\"10\" type=\"number\" min=\"1\"/></th><th><input id=\"number-of-splits\" value=\"4\" type=\"number\" min=\"1\"/></th><th><input id=\"max-value\" type=\"number\" min=\"1\" value=\"4\"/></th><th><input id=\"min-value\" type=\"number\" min=\"1\" value=\"1\"/></th><th><input id=\"run\" type=\"button\" value=\"Run\"/></th></tr></tbody>";
      var output = document.body.appendChild(document.createElement('pre'));
      output.style.overflowX = "scroll";
      document.getElementById("run").onclick = function() {
        splitXintoYComponentsBetweenMaxAndMin(parseInt(document.getElementById("number-to-split").value, 10), parseInt(document.getElementById("number-of-splits").value, 10), parseInt(document.getElementById("max-value").value, 10), parseInt(document.getElementById("min-value").value, 10))
          .then(function(data) {
            if (data !== false) {
              output.textContent += data.join("\t") + '\n';
            } else {
              output.textContent += 'Invalid data\n';
            }
          });
      };

      编辑1 - 大计算

      使用requestAnimationFramePromises代码现在可以异步执行,这样可以在不打扰用户的情况下延长计算时间。

      我还使用剩余范围制作了random函数比例,大大减少了大数字所需的计算量。

答案 2 :(得分:0)

这会计算1到4之间的随机数

将它包装在一个函数上,以满足您生成数组的需要

Math.floor(Math.random() * 4) + 1 

var randomNumber = Math.floor(Math.random() * 4) + 1 ;
console.log(randomNumber);

答案 3 :(得分:0)

太容易了。

core.create

答案 4 :(得分:0)

基本上您需要10的分区(请参阅https://en.wikipedia.org/wiki/Partition_(number_theory))并在结果集上应用您的条件。

// Partition generator taken from 
// https://gist.github.com/k-hamada/8aa85ac9b334fb89ac4f

function* partitions(n) {

    if (n <= 0) throw new Error('positive integer only');
    yield [n];

    var x = new Array(n);
    x[0] = n;
    for (var i = 1; i < n; i++) x[i] = 1;

    var m = 0, h = 0, r, t;
    while (x[0] != 1) {
        if (x[h] == 2) {
            m += 1;
            x[h] = 1;
            h -= 1;
        } else {
            r = x[h] - 1;
            x[h] = r;

            t = m - h + 1;
            while (t >= r) {
                h += 1;
                x[h] = r;
                t -= r;
            }
            m = h + (t !== 0 ? 1 : 0);
            if (t > 1) {
                h += 1;
                x[h] = t;
            }
        }
        yield x.slice(0, m + 1);
    }
}

results = [];
// Get all possible partitions for your number
for (var partition of partitions(10)) {
    // Apply your conditions (must be 4 numbers, none of them greater than 4)
    if(partition.length != 4 || partition.some((x) => x > 4)) continue;
    results.push(partition);
}
console.log(results);

答案 5 :(得分:0)

鉴于:

  

在总计为S的n个正数的集合中,其中至少一个小于S除以n(S / n)

,并且您想要一个正好是4个数字的结果集

您可以使用以下算法:

  1. 获取范围为[1,floor(S / n)]的随机数,在这种情况下,floor(10/4)= 2,因此获取范围为[1,2]的随机数。让我们将其标记为x1。
  2. 从范围[1,floor((S-x1)/(n-1))]中获取随机数。让我们将其标记为x2。
  3. 从范围[1,floor((S-x1-x2)/(n-2))]中获得一个随机数。
  4. 继续直到得到x(n-1)。
  5. 通过执行S-x1-x2 ....-x(n-1)获得最后一个数字。

最后,使用限制随机数上限的条件扩展上述算法。

只需 n个步骤,您就可以获取收藏集。

    function getRandomInt(min, max) {
       return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function getRandomCollection(min, max, length, sum) {
        var collection = [];
        var leftSum = sum - (min - 1);

        for(var i = 0; i < length - 1; i++) {
             var number = getRandomInt(min, Math.min(Math.ceil(leftSum/(length - i)), max));
             leftSum -= number;
             collection.push(number);
        }
        leftSum += min - 1;
        while(leftSum > max) {
             var randomIndex = Math.floor(Math.random() * collection.length);
             if(collection[randomIndex] < max) {
                  collection[randomIndex]++;
                  leftSum--;
             }
        }
        
        collection.push(leftSum);
        return collection;
    }
    console.log(getRandomCollection(1, 4, 4, 10).join(' + ') + ' = 10');
    console.log(getRandomCollection(3, 20, 10, 100).join(' + ') + ' = 100');

参考

My answer using the same algorithm for another question

答案 6 :(得分:0)

演出晚了一点,但是我发现这是一个值得思考的有趣任务,所以您现在就开始。我的方法不需要创建所有分区,也不必依赖于寻找随机匹配的纯粹运气,它很紧凑,应该没有偏见。

只要max的限制不太大,即使使用较大的值,它也可以有效地工作。

const len = 4;
const total = 10;
const max = 4;

let arr = new Array(len);
let sum = 0;
do {
  // get some random numbers
  for (let i = 0; i < len; i++) {
    arr[i] = Math.random();
  }
  // get the total of the random numbers
  sum = arr.reduce((acc, val) => acc + val, 0);
  // compute the scale to use on the numbers
  const scale = (total - len) / sum;
  // scale the array
  arr = arr.map(val => Math.min(max, Math.round(val * scale) + 1));
  // re-compute the sum
  sum = arr.reduce((acc, val) => acc + val, 0);
  // loop if the sum is not exactly the expected total due to scale rounding effects
} while (sum - total);

console.log(arr);