#javascript
#javascript
Вопрос:
Я пытаюсь создать функцию, которая возвращает массив случайных целых чисел, сумма которых фиксирована.
Вот мой код:
function arraySum(a) {
return a.reduce((a, b) => a b, 0)
}
function getRandomIntInclusive(min, max) {
const minCeil = Math.ceil(min)
const maxFloor = Math.floor(max)
return Math.floor(Math.random() * (maxFloor - minCeil 1)) minCeil
}
function randomNumbersWithFixedSum(quantity, sum) {
const randoms = [...Array(quantity - 1).keys()].map(q => getRandomIntInclusive(0, sum/quantity))
const last = sum - arraySum(randoms)
return [...randoms, last]
}
console.log(randomNumbersWithFixedSum(1, 100))
console.log(randomNumbersWithFixedSum(2, 100))
console.log(randomNumbersWithFixedSum(3, 100))
console.log(randomNumbersWithFixedSum(4, 100))
console.log(randomNumbersWithFixedSum(5, 100))
Это работает, но это не совсем то, что я хочу. Я бы хотел, чтобы каждое число было случайным в диапазоне [0, sum]
.
В randomNumbersWithFixedSum
функции я принудительно ввел первые (quantity-1)
числа [0, sum/quantity]
, но мне это не нравится.
Как я могу создать действительно случайные числа [0, sum]
, сумма которых равна sum
?
Комментарии:
1. Если числа находятся в диапазоне
[0,sum]
, вы получите много нулей. Это нормально?2. Нет, я полагаю, у меня много 0, только если
sum=0
3. Тогда невозможно иметь N случайных чисел из [0, sum], которые суммируются до суммы. Они не могут быть случайными. Что-то должно дать. Какое приложение для этого?
4. Например, рассмотрим 5 случайных чисел от 0 до 10. Первое число равно 7. Следующее число должно быть от 0 до 3, а не от 0 до 10.
5. Конечно! Именно по этой причине я задаю этот вопрос. Я нашел решение (протестируйте мой код), но я спрашиваю, есть ли лучший и более правильный способ сделать это. Если рассмотреть число 5 в [0 10], а первое равно 10, то результат должен быть
[10, 0, 0, 0, 0]
, я согласен с вами, но это предельный случай, потому что первое число должно быть 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 или 10
Ответ №1:
Это яркий пример, когда рекурсивная функция может упростить проблему. Каждое выполнение вычисляет только одно случайное число, а затем вызывает себя с обновленными аргументами. Описание см. В комментариях.
function getRandomNumberBetweenIncluding(min, max) {
return Math.floor(Math.random() * (max - min 1)) min;
}
function randomNumbersWithFixedSum(quantity, sum) {
// only a single number required; return the passed sum.
if (quantity === 1) {
return [sum];
}
// Create one random number and return an array containing that number
// as first item. Then use the spread operator and recursively execute
// the function again with a decremented quantity and the updated
// maximum possible sum.
const randomNum = getRandomNumberBetweenIncluding(0, sum);
return [
randomNum,
...randomNumbersWithFixedSum(quantity - 1, sum - randomNum),
];
}
console.log(randomNumbersWithFixedSum(1, 100));
console.log(randomNumbersWithFixedSum(2, 100));
console.log(randomNumbersWithFixedSum(3, 100));
console.log(randomNumbersWithFixedSum(4, 100));
console.log(randomNumbersWithFixedSum(5, 100));
Ответ №2:
Как насчет следующего решения:
- На первой итерации мы пытаемся получить случайное число между
0
иmax
— скажем, мы извлекаемN
. - На второй итерации — максимально возможное значение не может быть больше
max
—N
(в противном случае сумма будет большеmax
). - Продолжайте
quantity - 1
шаги. - На последнем шаге мы должны использовать то, что осталось до
max
function getRandomIntInclusive(min, max) {
const minCeil = Math.ceil(min)
const maxFloor = Math.floor(max)
return Math.floor(Math.random() * (maxFloor - minCeil 1)) minCeil
}
function randomNumbersWithFixedSum(quantity, sum) {
const result = [];
let total = 0;
for (let i = 0; i < quantity - 1; i ) {
let max = sum - total;
let num = getRandomIntInclusive(0, max);
result.push(num);
total = num;
}
result.push(sum - total);
return resu<
}
console.log(randomNumbersWithFixedSum(1, 100))
console.log(randomNumbersWithFixedSum(2, 100))
console.log(randomNumbersWithFixedSum(3, 100))
console.log(randomNumbersWithFixedSum(4, 100))
console.log(randomNumbersWithFixedSum(5, 100))
Ответ №3:
Вы могли бы взять только остальную часть суммы для максимального случайного числа и перетасовать массив позже, чтобы опустить большие значения только в верхней части массива.
function shuffle(array) {
let i = array.length;
while (--i) {
let j = Math.floor(Math.random() * (i 1)),
temp = array[j];
array[j] = array[i];
array[i] = temp;
}
return array;
}
function getRandomIntInclusive(min, max) {
const minCeil = Math.ceil(min)
const maxFloor = Math.floor(max)
return Math.floor(Math.random() * (maxFloor - minCeil 1)) minCeil
}
function randomNumbersWithFixedSum(length, sum) {
length--;
const randoms = Array.from({ length }, q => {
const r = getRandomIntInclusive(0, sum)
sum -= r;
return r;
});
return shuffle([...randoms, sum]);
}
console.log(randomNumbersWithFixedSum(5, 100))
console.log(randomNumbersWithFixedSum(4, 100))
console.log(randomNumbersWithFixedSum(3, 100))
console.log(randomNumbersWithFixedSum(2, 100))
console.log(randomNumbersWithFixedSum(1, 100))
.as-console-wrapper { max-height: 100% !important; top: 0; }