JavaScript всегда генерирует одни и те же числа во втором цикле for [закрыто]

Вопрос или проблема

Скрипт:

let col1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] 
let col2 = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
for(let i = 0; i < 5; i++) {
    const random = Math.floor(Math.random() * col1.length + 1)
    const index = col1.indexOf(random)
    const number = col1.at(index)
    col1.splice(index, 1)
    console.log(number)
}
for(let i = 0; i < 5; i++) {
    const random = Math.floor(Math.random() * col2.length + 1)
    const index = col2.indexOf(random)
    const number = col2.at(index)
    col2.splice(index, 1)
    console.log(number)
}

Первый цикл for всегда генерирует 5 случайных чисел из первого массива. Но второй цикл for всегда показывает одни и те же результаты (30, 29, 28, 27, 26). Что вызывает такое поведение?

const random = Math.floor(Math.random() * col2.length + 1);

генерирует случайное целое число от 0 до 15, так как col2.length равно 15. Затем,

const index = col2.indexOf(random);

всегда возвращает -1, так как в col2 нет элемента со значением random. Поэтому

const number = col2.at(index);

всегда присваивает number последний элемент col2, который меняется с каждой итерацией, так как

col2.splice(index, 1);

удаляет последний элемент col2.

Если вы хотите получить пять различных элементов из каждого массива, вы можете просто сделать:

let col1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] 
let col2 = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

for(let i = 0; i < 5; i++) {
    const index = Math.floor(Math.random() * col1.length);
    const number = col1.at(index);
    col1.splice(index, 1);
    console.log(number);
}
for(let i = 0; i < 5; i++) {
    const index = Math.floor(Math.random() * col2.length);
    const number = col2.at(index);
    col2.splice(index, 1);
    console.log(number);
}

ИЗМЕНИТЬ: убран +1 в Math.floor(), чтобы оставаться в пределах диапазона.

Вы пытаетесь получить индекс значения в col2. У вас массив из 15 элементов. Ваш индекс всегда будет меньше 16 (самого низкого числа во втором наборе), и, соответственно, col2.indexOf(random) всегда будет возвращать -1, так как ваше случайное значение меньше 16 не содержится в этом массиве, и indexOf возвращает -1, если он не может найти индекс заданного значения. Удаление с -1 вернет последнее значение в вашем массиве, вот почему вы получаете 30-26. См. документацию MDN для indexOf:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf

Ваш второй цикл for фактически вызывает at() с -1 пять раз:

const number = col2.at(-1)

Когда вы передаете -1 в .at(), он возвращает последний элемент вашего массива, а так как следующий вызов .splice(-1, 1) удаляет последний элемент из col2, следующая итерация даст вам следующий последний элемент и так далее.

Основная проблема вашего кода в том, что вы обращаетесь к random как к элементу вашего массива, а на самом деле это индекс (если вы уберете +1). Это приводит к тому, что ваш код .indexOf() не может найти элемент random в вашем массиве, и, следовательно, устанавливает index в -1. Единственная причина, по которой это работает в вашем первом примере, заключается в том, что ваши элементы последовательны, начиная с 1, так что в этом случае random совпадает с элементом вашего массива. Но в более общем случае (как col2), это не так.

Вместо этого вы можете получить свой случайный индекс, получить элемент по этому индексу, используя квадратные скобки (нет необходимости в .indexOf() или .at()), а затем удалить элемент из вашего исходного массива (чтобы избежать повторного использования одного и того же элемента):

let col2 = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
for(let i = 0; i < 5; i++) {
    const randomIndex = Math.floor(Math.random() * col2.length);
    const number = col2[randomIndex];
    col2.splice(randomIndex, 1)
    console.log(number);
}

Или альтернативный подход — перемешать ваш массив, а затем использовать .slice(), чтобы получить первые n элементов:

let col2 = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];

// Источник; https://stackoverflow.com/a/12646864/5648954
function shuffle(array) {
    for (var i = array.length - 1; i >= 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

const n = 5;
const res = shuffle(col2).slice(0, n);
console.log(res);

Ответ или решение

При разработке на JavaScript, важно учитывать, как генерируются случайные числа и как они используются для извлечения элементов из массивов. В приведённом вами коде действительно существует проблема, из-за которой второй цикл for всегда возвращает одни и те же значения (30, 29, 28, 27, 26) в каждый из своих повторений. Давайте разберёмся в этом более подробно.

Проблема с кодом

В вашем втором цикле вы используете следующий код для получения случайного числа из массива col2:

const random = Math.floor(Math.random() * col2.length + 1);
const index = col2.indexOf(random);

Ошибка в расчете случайного числа

Первое, что стоит отметить, это то, что выражение Math.floor(Math.random() * col2.length + 1) генерирует случайное число от 1 до 15 (включительно), поскольку col2.length равно 15. Однако элементы в массиве col2 находятся в диапазоне от 16 до 30. Таким образом, indexOf никогда не найдет совпадений, так как значения, которые вы генерируете, не соответствуют значениям в массиве col2. В результате col2.indexOf(random) всегда возвращает -1.

Влияние на внедрение индекса в массив

Далее, когда index равен -1, строка:

const number = col2.at(index);

возвращает последний элемент массива col2, так как at(-1) возвращает последний элемент. После этого col2.splice(index, 1) удаляет последний элемент массива (или, в данном случае, элемент с индексом -1), что и объясняет, почему вы получаете 30, 29, 28, 27, 26 в результате выполнения этого цикла.

Решение проблемы

Чтобы исправить это, вам необходимо скорректировать способ генерации случайного числа и получения индекса элемента. Вместо использования indexOf, получите случайный индекс напрямую из массива. Вот исправленный код:

let col1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 
let col2 = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];

// С первого массива
for(let i = 0; i < 5; i++) {
    const index = Math.floor(Math.random() * col1.length);
    const number = col1[index];
    col1.splice(index, 1);
    console.log(number);
}

// Со второго массива
for(let i = 0; i < 5; i++) {
    const index = Math.floor(Math.random() * col2.length);
    const number = col2[index];
    col2.splice(index, 1);
    console.log(number);
}

Альтернативный подход

Другой подход для выбора нескольких уникальных элементов из массива — это "перемешивание" массива и затем извлечение первых N элементов. Например:

function shuffle(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]]; // Меняем местами элементы
    }
    return array;
}

const n = 5;
const sampledNumbers = shuffle(col2).slice(0, n);
console.log(sampledNumbers);

Заключение

Таким образом, ошибка в вашем исходном коде вызвана некорректным путем генерации случайного значения, что приводит к постоянному получению одного и того же набора значений. Следуя описанным рекомендациям, вы сможете решить проблему и корректно генерировать уникальные случайные числа из ваших массивов.

Оцените материал
Добавить комментарий

Капча загружается...