Я заметил, что функция библиотеки rand()
, когда она вызывается только один раз в цикле, она почти всегда производит положительные числа.
for (i = 0; i < 100; i++) {
printf("%d\n", rand());
}
Но когда я добавляю два вызова rand()
, генерируемые числа теперь имеют больше отрицательных чисел.
for (i = 0; i < 100; i++) {
printf("%d = %d\n", rand(), (rand() + rand()));
}
Может кто-нибудь объяснить, почему во втором случае я вижу отрицательные числа?
PS: Я инициализирую семя перед циклом как srand(time(NULL))
.
Ответ 1
rand()
определено для возврата целого числа между 0
и RAND_MAX
.
rand() + rand()
может переполняться. То, что вы наблюдаете, вероятно, является результатом undefined поведения, вызванного переполнением целых чисел.
Ответ 2
Проблема заключается в добавлении. rand()
возвращает значение int
0...RAND_MAX
. Итак, если вы добавите два из них, вы получите до RAND_MAX * 2
. Если это превышает INT_MAX
, результат добавления переполняет допустимый диапазон, который может содержать int
. Переполнение значных значений undefined и может привести к тому, что ваша клавиатура будет разговаривать с вами на иностранных языках.
Поскольку здесь нет выигрыша при добавлении двух случайных результатов, простая идея состоит в том, чтобы просто не делать этого. В качестве альтернативы вы можете сделать каждый результат до unsigned int
до добавления, если это может удерживать сумму. Или используйте больший тип. Обратите внимание, что long
не обязательно шире, чем int
, то же самое относится к long long
, если int
не менее 64 бит!
Заключение: просто избегайте добавления. Это не дает больше "случайности". Если вам нужно больше битов, вы можете объединить значения sum = a + b * (RAND_MAX + 1)
, но для этого, вероятно, также потребуется больший тип данных, чем int
.
Как ваша заявленная причина состоит в том, чтобы избежать нулевого результата: этого нельзя избежать, добавив результаты двух вызовов rand()
, так как оба могут быть равны нулю. Вместо этого вы можете просто увеличить. Если RAND_MAX == INT_MAX
, это невозможно сделать в int
. Однако (unsigned int)rand() + 1
сделает очень, очень вероятно. Вероятно (не окончательно), потому что он требует UINT_MAX > INT_MAX
, который я не уверен, гарантирован, но действителен для всех реализаций, о которых я действительно знаю (и это не просто x86/64 или ARM).
Внимание:
Несмотря на то, что здесь уже посыпаны комментарии, обратите внимание, что добавление двух случайных значений не получает равномерного распределения, но треугольное распределение, например, перекатывание двух кубиков: чтобы получить 12
(две кости), обе кости должны показать 6
, для 11
уже есть два возможных варианта: 6 + 5
или 5 + 6
и т.д.
Таким образом, добавление также плохо из этого аспекта.
Также обратите внимание, что генерируемые результаты rand()
не являются независимыми друг от друга, поскольку они генерируются генератором псевдослучайных чисел . Отметим также, что стандарт не определяет качество или равномерное распределение вычисленных значений.
Ответ 3
Это ответ на разъяснение вопроса, сделанного в комментарии к этому ответу,
причина, по которой я добавлял, заключалась в том, чтобы избежать "0" в качестве случайного числа в моем коде. rand() + rand() - это быстрое грязное решение, которое легко пришло мне в голову.
Проблема заключалась в том, чтобы избежать 0. Существует (по крайней мере) две проблемы с предлагаемым решением. Один из них, как показывают другие ответы, указывает, что rand()+rand()
может вызывать поведение undefined. Лучший совет - никогда не ссылаться на поведение undefined. Другая проблема заключается в том, что нет гарантии, что rand()
не будет генерировать 0 дважды подряд.
Следующие отклонения ноль, избегают поведения undefined и в подавляющем большинстве случаев будут быстрее, чем два вызова rand()
:
int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);
Ответ 4
В основном rand()
производят числа между 0
и RAND_MAX
и 2 RAND_MAX > INT_MAX
в вашем случае.
Вы можете использовать модуль с максимальным значением вашего типа данных, чтобы предотвратить переполнение. Это, конечно, нарушит распределение случайных чисел, но rand
- это просто способ получить быстрые случайные числа.
#include <stdio.h>
#include <limits.h>
int main(void)
{
int i=0;
for (i=0; i<100; i++)
printf(" %d : %d \n", rand(), ((rand() % (INT_MAX/2))+(rand() % (INT_MAX/2))));
for (i=0; i<100; i++)
printf(" %d : %ld \n", rand(), ((rand() % (LONG_MAX/2))+(rand() % (LONG_MAX/2))));
return 0;
}
Ответ 5
Возможно, вы могли бы попробовать довольно хитрый подход, убедившись, что значение, возвращаемое суммой 2 rand(), никогда не превышает значение RAND_MAX. Возможным подходом может быть сумма = rand()/2 + rand()/2; Это обеспечит, что для 16-битного компилятора с RAND_MAX значением 32767, даже если оба rand возвращаются 32767, даже тогда (32767/2 = 16383) 16383 + 16383 = 32766, таким образом, не приведет к отрицательной сумме.
Ответ 6
Чтобы избежать 0, попробуйте следующее:
int rnumb = rand()%(INT_MAX-1)+1;
Вам нужно включить limits.h.
Извините за мой плохой английский.
Ответ 7
причина, по которой я добавлял, заключалась в том, чтобы избежать "0" в качестве случайного числа в моем коде. rand() + rand() - это быстрое грязное решение, которое легко пришло мне в голову.
Простое решение (хорошо, назовите его "Hack" ), которое никогда не производит нулевой результат и никогда не будет переполняться:
x=(rand()/2)+1 // using divide -or-
x=(rand()>>1)+1 // using shift which may be faster
// compiler optimization may use shift in both cases
Это ограничит ваше максимальное значение, но если вас это не волнует, тогда это будет хорошо работать для вас.
Ответ 8
В то время как все остальные говорили о вероятном переполнении, вполне могут быть причиной отрицательного, даже если вы используете целые числа без знака. Реальная проблема заключается в том, что в качестве семени используется функция времени/даты. Если вы действительно познакомитесь с этой функциональностью, вы точно поймете, почему я это говорю. Как то, что он на самом деле делает, это дать дистанцию (прошедшее время) с определенной даты/времени. Хотя использование функции даты/времени в качестве семени для rand(), является очень распространенной практикой, это действительно не самый лучший вариант. Вы должны искать лучшие альтернативы, так как существует множество теорий по этой теме, и я не мог уйти во все их. Вы добавляете в это уравнение возможность переполнения, и этот подход был обречен с самого начала.
Те, кто разместил rand() + 1, используют решение, которое больше всего используется, чтобы гарантировать, что они не получат отрицательное число. Но этот подход действительно не самый лучший способ.
Лучшее, что вы можете сделать, - это дополнительное время для написания и использования правильной обработки исключений и только добавление к номеру rand(), если и/или когда вы закончите с нулевым результатом. И, чтобы справиться с отрицательными цифрами должным образом. Функциональность rand() не идеальна и поэтому должна использоваться в сочетании с обработкой исключений, чтобы гарантировать, что вы получите желаемый результат.
Привлечение дополнительного времени и усилий для исследования, изучения и правильного внедрения функций rand() - это хорошо стоит времени и усилий. Только мои два цента. Удачи в ваших начинаниях...