Почему 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, результат добавления переполняет допустимый диапазон an 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, etc.

Итак, добавление также плохо от этого аспект.

также обратите внимание, что результаты 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() это стоит времени и усилий. Только мои два цента. Удачи в ваших начинаниях...