Не генерируйте одинаковые случайные числа в C++ 2

srand(time(NULL));

for(i = 0; i < 100; i++)
{
   int randomNumber = (rand() % 100) + 1  // gets a random number from 0 - 100.
   // do stuff
}

В настоящее время у меня есть переменная случайного числа, которая хранит число от 0 до 100 на каждой итерации цикла for, а приведенный ниже код затем выполняет действия с этой переменной случайного числа. Однако я заметил, что с каждой итерацией цикла иногда генерируется одно и то же случайное число, а иногда оно генерирует число, очень близкое к тому, которое я уже сгенерировал. Например: когда
i = 1 randomNumber может быть 50, но когда i = 2, это может быть 51. Это очень раздражает для того, что я кодирую. Есть ли способ сделать так, чтобы, если он генерирует 50 в первый раз, во второй раз он должен был генерировать целое число 2 от только что сгенерированного. Например. если он просто генерирует 50, то во второй раз он не может сгенерировать 48,49,50,51,52, но все равно генерирует от 0 до 100, просто пропуская эти числа. Затем он повторяет то же самое, когда генерируется новый randomNumber.


person Sawy3r    schedule 03.05.2019    source источник
comment
поэтому просто зацикливайте, пока у вас не будет подходящего числа.   -  person Antti Haapala    schedule 03.05.2019
comment
@xing: проблема не в этом.   -  person Eric Postpischil    schedule 03.05.2019
comment
@KenWhite: это явно запрос на распределение, отличное от одной из независимых равномерных выборок по [1, 100]. Да, выборки, выбранные в зависимости от предыдущих выборок, могут быть случайными.   -  person Eric Postpischil    schedule 03.05.2019
comment
@EricPostpischil, конечно, но, учитывая, что srand(time(NULL)), вероятно, является причиной дубликатов, все уже является улучшением: D   -  person Antti Haapala    schedule 03.05.2019
comment
Какое поведение вы хотите, когда предыдущий образец был 100? Должны ли 1 или 2 считаться близкими к этому, потому что они заворачиваются? Или следует исключить только 98, 99 и 100?   -  person Eric Postpischil    schedule 03.05.2019
comment
@AnttiHaapala: Нет, srand(time(NULL)) не является причиной дубликатов. В вопросе ясно показан цикл, выбирающий 100 сэмплов без промежуточных srand, и он спрашивает об сэмплах «с каждой итерацией цикла», а не с различными выполнениями всего цикла или программы.   -  person Eric Postpischil    schedule 03.05.2019
comment
Если вам нужно 100 чисел от 1 до 100 без дубликатов, заполните массив числами от 1 до 100 и перетасуйте его.   -  person Retired Ninja    schedule 03.05.2019
comment
Вы просто не хотите, чтобы соседние номера были последовательными? Так что все в порядке, если это идет 10 20 10 20 10 20, потому что они больше, чем 2 друг от друга?   -  person Barmar    schedule 03.05.2019
comment
Проблемы, которые вы описываете, являются именно свойствами случайных чисел, поэтому очевидно, что вам не нужны случайные числа. Опишите нам, что вы на самом деле хотите сделать, и мы могли бы помочь вам сделать это.   -  person Lee Daniel Crocker    schedule 03.05.2019
comment
@LeeDanielCrocker: потенциальные выборки, близкие к предыдущим выборкам, являются свойствами равномерного распределения независимых выборок, а не случайных чисел в целом. Вопрос описывает, что они на самом деле хотят сделать: исключить числа рядом с предыдущей выборкой из распределения для текущей выборки.   -  person Eric Postpischil    schedule 04.05.2019
comment
Код (изначально) генерирует числа от 1 до 100, но в комментарии написано от 0 до 100. Что нужно?   -  person Eric Postpischil    schedule 04.05.2019
comment
@EricPostpischil Он даже не сел в самолет, летящий в страну, чтобы сказать, что он действительно хочет сделать. Он ничего не сказал, но у меня есть этот номер, он так себя ведет, я хочу, чтобы он так себя вел. ПОЧЕМУ он этого хочет? Пытается ли он изобразить случайно выглядящие точки? Он пытается выбрать полуслучайные вопросы для викторины? Он пытается перетасовать музыкальные подборки? Это то, что нам нужно знать, чтобы помочь.   -  person Lee Daniel Crocker    schedule 04.05.2019
comment
@LeeDanielCrocker: Некоторое подтверждение и разъяснение помогут, но вполне вероятно, что запрос состоит в том, чтобы сделать первую выборку из равномерного распределения целых чисел в [1, 100] и сделать последующие выборки из равномерного распределения целых чисел в [ 1, 100]-[x-2, x+2], где x — предыдущая выборка. Несмотря на это, ваше утверждение о том, что свойства являются «именно свойствами случайных чисел», является ложным и бесполезным.   -  person Eric Postpischil    schedule 04.05.2019


Ответы (2)


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

srand(time(NULL));
int lastRandom = -2;
for(int i = 0; i < 100; i++)
{
    int randomNumber;
    do {
        randomNumber = (rand() % 100) + 1;  // gets a random number from 0 - 100.
    } while (randomNumber >= lastRandom - 2 && randomNumber <= lastRandom + 2);
    lastRandom = randomNumber;
    // do stuff
}

Обычно я бы не советовал вызывать rand() в цикле, потому что это может продолжаться слишком долго, если критерии приемлемости слишком строгие. Но это отклоняет только около 5%, поэтому вряд ли это повторится много раз.

person Barmar    schedule 03.05.2019
comment
Добавлен недостающий ; из исходного кода. - person Barmar; 04.05.2019

Что вы можете сделать, если вы не возражаете против получения одного и того же числа более одного раза (если оно не находится в строке), но вы не хотите, чтобы внутренний цикл пытался получить новый номер:

  1. Получить случайное число от 1 до 100 обычно в первом цикле.

  2. После этого, добавив случайное число от 3 до 97 к последнему числу, а затем используя модуль 100, вы получите случайное число в диапазоне от 1 до 100, исключая числа в диапазоне от oldNum - 2 до oldNum + 2.

    (Обратите внимание, что это оборачивает диапазон исключенных чисел, так что, например, если последним числом было 100, то 1 и 2 также будут исключены).

srand(time(NULL));

int randomNumber;
for(i = 0; i < 100; i++) {
   if (i == 0) {
       randomNumber = (rand() % 100) + 1;  // gets a random number from 1 - 100.
   } else {
       randomNumber = ((randomNumber + (rand() % 95) + 3) % 100) + 1;
   }
   // do stuff
}

Если вы не хотите, чтобы список исключенных номеров переносился (например, если последним номером было 99, вы хотели бы исключить только 97, 98, 99 и 100), то это немного сложнее:

  1. Вы все еще хотите получить случайное число от 1 до 100, как правило, в первом цикле.

  2. После этого по умолчанию будет добавлено случайное число от 3 до 97 к последнему числу, а затем использовано по модулю 100, чтобы получить случайное число в диапазоне от 1 до 100, исключая числа в диапазоне от oldNum - 2 до oldNum + 2.

    Но прежде чем перейти к параметрам по умолчанию, вам нужно проверить, является ли старое число настолько низким или настолько высоким, что диапазон исключенных чисел будет охватывать:

    а. Если старое число настолько низкое, что диапазон исключенных чисел будет обернут, нам нужно случайное число в диапазоне от oldNum + 3 до 100.

    б. Если старое число настолько велико, что диапазон исключенных чисел переносится, нам нужно случайное число в диапазоне от 1 до oldNum - 3.

srand(time(NULL));

int randomNumber;
for(i = 0; i < 100; i++) {
   if (i == 0) {
       randomNumber = (rand() % 100) + 1;  // gets a random number from 1 - 100.
   } else {
        if (randomNumber < 3) {
        /* Old number is so low that excluded numbers would wrap if we would
         * be using the 'default' way.
         * We want a random number in the range from randomNumber + 3 to 100:
         */
            randomNumber += (rand() % (98 - randomNumber)) + 3; 
        }
        else if (randomNumber > 98) {
        /* Old number is so high that excluded numbers would wrap if we would
         * be using the 'default' way.
         * We want a random number in the range from 1 to randomNumber - 3:
         */
            randomNumber = (rand() % (randomNumber - 3)) + 1; 
        } else {
        /* Default way. 
         * We want a random number in the range 1 to 100, excluding numbers in the 
         * range randomNumber - 2 to randomNumber + 2
         */
            randomNumber = ((randomNumber + (rand() % 95) + 3) % 100) + 1;
        }
   }
   // do stuff
}

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

srand(time(NULL));

int randomNumber;
int const maxNum = 100; // Random number must be in the range from 1 to maxNum
int const exclude = 2;  // New random number must NOT be in the range
                        // from (oldNum - exclude) to (oldNum + exclude)
for(i = 0; i < 100; i++) {
   if (i == 0) {
       randomNumber = (rand() % maxNum) + 1;  // gets a random number from 1 to maxNum.
   } else {
        if (randomNumber <= exclude) {
        /* Old number is so low that excluded numbers would wrap if we would
         * be using the 'default' way.
         * We want a random number in the range from (randomNumber + exclude + 1)
         * to maxNum:
         */
            randomNumber += (rand() % (maxNum - exclude - randomNumber)) + exclude + 1; 
        }
        else if (randomNumber > maxNum - exclude) {
        /* Old number is so high that excluded numbers would wrap if we would
         * be using the 'default' way.
         * We want a random number in the range from 1 to (randomNumber - exclude - 1):
         */
            randomNumber = (rand() % (randomNumber - exclude - 1)) + 1; 
        } else {
        /* Default way. 
         * We want a random number in the range 1 to 100, excluding numbers in the 
         * range randomNumber - exclude to randomNumber + exclude
         */
            randomNumber = ((randomNumber + (rand() % (maxNum - 2*exclude - 1) 
                    + exclude + 1) % maxNum) + 1;
        }
   }
   // do stuff
}
person SiggiSv    schedule 03.05.2019