Почему rand () + rand () производит отрицательные числа?
я заметил:rand() библиотечная функция когда она вызывается только один раз в цикле, она почти всегда производит положительные числа.
for (i = 0; i < 100; i++) {
printf("%dn", rand());
}
но когда я добавляю два rand() вызовы, генерируемые номера теперь имеют больше отрицательных чисел.
for (i = 0; i < 100; i++) {
printf("%d = %dn", rand(), (rand() + rand()));
}
может кто-нибудь объяснить мне, почему я вижу отрицательные числа во втором случае?
PS: Я инициализирую семя перед циклом как srand(time(NULL)).
8 ответов:
rand()определяется для возврата целого числа между0иRAND_MAX.rand() + rand()может переполниться. То, что вы наблюдаете, скорее всего, является результатом неопределено поведение вызвано переполнением целого числа.
проблема заключается в добавлении.
rand()возвращаетintстоимостью0...RAND_MAX. Так что, если вы добавите два из них, вы получите доRAND_MAX * 2. Если это превышаетINT_MAX, в результате сложения превышает допустимые значения элементаintможно провести. Переполнение подписанных значений является неопределенным поведением и может привести к тому, что ваша клавиатура будет говорить с вами на иностранных языках.поскольку здесь нет никакого выигрыша в добавлении двух случайных результатов, простая идея состоит в том, чтобы просто не делать этого. В качестве альтернативы вы можете привести каждый результат к
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, что верно для всех реализаций, о которых я знаю (который охватывает довольно некоторые встроенные архитектуры, DSPs и все настольные, мобильные и серверные платформы последних 30 годы.)предупреждение:
хотя уже посыпали в комментариях здесь, обратите внимание, что добавление двух случайных значений делает не получить равномерное распределение, но треугольное распределение, как прокатка двух кубиков: чтобы получить
12(две кости) обе кости должны показать6. ибо11уже есть два возможных варианта:6 + 5или5 + 6и т. д.Итак, добавление также плохо от этого аспект.
также обратите внимание, что результаты
rand()генерирует не являются независимыми друг от друга, так как они генерируются генератор псевдослучайных чисел. Обратите также внимание, что стандарт не определяет качество или равномерное распределение рассчитанных значений.
это ответ на уточнение вопроса, сделанное в комментарии к ответ,
причина, по которой я добавлял, заключалась в том, чтобы избежать " 0 " как случайного числа в моем коде. rand ()+rand () было быстрое грязное решение, которое легко пришло мне на ум.
проблема заключалась в том, чтобы избежать 0. Существует (по крайней мере) две проблемы с предлагаемым решением. Один из них, как показывают другие ответы, что
rand()+rand()может вызвать неопределенное поведение. Лучший совет-никогда не вызывать неопределенное поведение. Другая проблема заключается в том, что нет никакой гарантии, чтоrand()не будет производить 0 дважды подряд.следующее отклоняет ноль, избегает неопределенного поведения, и в подавляющем большинстве случаев будет быстрее, чем два вызова
rand():int rnum; for (rnum = rand(); rnum == 0; rnum = rand()) {} // or do rnum = rand(); while (rnum == 0);
в принципе
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; }
может быть, вы можете попробовать довольно сложный подход, гарантируя, что значение, возвращаемое суммой 2 rand (), никогда не превышает значение RAND_MAX. Возможным подходом может быть sum = rand () / 2 + rand () / 2; это гарантирует, что для 16-битного компилятора со значением RAND_MAX 32767 даже если оба rand возвращают 32767, даже тогда (32767/2 = 16383) 16383+16383 = 32766, таким образом, не приведет к отрицательной сумме.
причина, по которой я добавлял, заключалась в том, чтобы избежать " 0 " как случайного числа в моем коде. rand ()+rand () было быстрое грязное решение, которое легко пришло мне на ум.
простое решение (хорошо, назовите его "взломом"), которое никогда не дает нулевого результата и никогда не переполнится:
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это ограничит максимальную стоимость, но если вы не заботитесь об этом, то это должно работать нормально для вас.
чтобы избежать 0, попробуйте следующее:
int rnumb = rand()%(INT_MAX-1)+1;вы должны включить
limits.h.
пока что все говорит о вероятном переполнении вполне может быть причиной негатива, даже когда вы используете целые числа без знака. Реальная проблема заключается в использовании функции времени / даты в качестве семени. Если вы действительно знакомы с этой функциональностью, вы будете точно знать, почему я говорю это. А что он делает, это дает расстояние (время) с заданной даты/времени. В то время как использование функции даты / времени в качестве семени для rand () является очень распространенным практика, это действительно не лучший вариант. Вы должны искать лучшие альтернативы, так как есть много теорий на эту тему, и я не мог вдаваться во все из них. Вы добавляете в это уравнение возможность переполнения, и этот подход был обречен с самого начала.
те, которые разместили rand ()+1, используют решение, которое большинство использует, чтобы гарантировать, что они не получат отрицательное число. Но, этот подход действительно не лучший способ либо.
в лучшее, что вы можете сделать, это потратить дополнительное время на запись и использование правильной обработки исключений, и только добавить к номеру rand (), если и/или когда вы получите нулевой результат. И, чтобы иметь дело с отрицательными числами должным образом. Функциональность rand () не идеальна, и поэтому ее необходимо использовать в сочетании с обработкой исключений, чтобы гарантировать, что вы получите желаемый результат.
требуется дополнительное время и усилия для изучения, изучения и правильной реализации функциональности rand() это хорошо стоит времени и усилий. Только мои два цента. Удачи вам в ваших начинаниях...
Comments