¿Por qué rand() + rand() producen números negativos?

10 minutos de lectura

avatar de usuario
loco

observé que rand() función de biblioteca cuando se llama solo una vez dentro de un ciclo, casi siempre produce números positivos.

for (i = 0; i < 100; i++) {
    printf("%d\n", rand());
}

Pero cuando sumo dos rand() llamadas, los números generados ahora tienen más números negativos.

for (i = 0; i < 100; i++) {
    printf("%d = %d\n", rand(), (rand() + rand()));
}

¿Alguien puede explicar por qué veo números negativos en el segundo caso?

PD: inicializo la semilla antes del ciclo como srand(time(NULL)).

  • rand() no puede ser negativo…

    – veinte limón

    13 de junio de 2015 a las 17:17

  • rand() + rand() puede fluir

    – maskacovnik

    13 de junio de 2015 a las 17:17

  • Que es RAND_MAX para su compilador? Por lo general, puedes encontrarlo en stdlib.h. (Gracioso: comprobando man 3 randlleva la descripción de una línea “generador de números aleatorios incorrecto”.)

    – Jongware

    13 de junio de 2015 a las 17:26


  • hacer lo que todo programador cuerdo haría abs(rand()+rand()). ¡Prefiero tener un UB positivo que uno negativo! 😉

    – Vinicius Kamakura

    15 de junio de 2015 a las 17:32

  • @hexa: esa no es una solución para la UB, como ya ocurre con la adición. No puedes hacer que UB se convierta comportamiento definido. A cuerdo progrtammer evitaría UB como el infierno.

    – demasiado honesto para este sitio

    15 de junio de 2015 a las 18:07


rand() se define para devolver un número entero entre 0 y RAND_MAX.

rand() + rand()

podría desbordarse. Lo que observa es probablemente el resultado de comportamiento indefinido causado por el desbordamiento de enteros.

  • @JakubArnold: ¿Cómo es que cada idioma especifica el comportamiento de desbordamiento de manera diferente? Python, por ejemplo, no tiene ninguno (bueno, hasta la memoria disponible), ya que int simplemente crece.

    – demasiado honesto para este sitio

    15 de junio de 2015 a las 18:09


  • @Olaf Depende de cómo un idioma decida representar enteros con signo. Java no tenía ningún mecanismo para detectar el desbordamiento de enteros (hasta Java 8) y lo definió para envolver y Vamos usa la representación del complemento a 2 únicamente y la define legal para desbordamientos de enteros con signo. C obviamente admite más del complemento a 2.

    – PP

    15 de junio de 2015 a las 18:28


  • @EvanCarslake No, ese no es un comportamiento universal. Lo que dices es sobre la representación en complemento a 2. Pero el lenguaje C también permite otras representaciones. La especificación del lenguaje C dice que el desbordamiento de enteros con signo es indefinido. Entonces, en general, ningún programa debe confiar en tal comportamiento y necesita codificar con cuidado para no causar un desbordamiento de enteros con signo. Pero esto no es aplicable para enteros sin signo, ya que se “envolverían” de una manera bien definida (módulo de reducción 2). [continued]…

    – PP

    16 de junio de 2015 a las 17:19


  • Esta es la cita del estándar C relacionada con el desbordamiento de enteros con signo: Si ocurre una condición excepcional durante la evaluación de una expresión (es decir, si el resultado no está definido matemáticamente o no está en el rango de valores representables para su tipo), el comportamiento es indefinido.

    – PP

    16 de junio de 2015 a las 17:19

  • @EvanCarslake alejándose un poco de la pregunta, los compiladores de C usan el estándar y para los enteros con signo pueden asumir que a + b > a si saben eso b > 0. También pueden suponer que si hay una declaración ejecutada más tarde a + 5 entonces el valor actual es más bajo que INT_MAX - 5. Entonces, incluso en el procesador/intérprete de complemento de 2 sin trampas, el programa podría no comportarse como si ints eran complemento a 2 sin trampas.

    – Maciej Piechotka

    18 de junio de 2015 a las 8:02

avatar de usuario
demasiado honesto para este sitio

El problema es la adición. rand() devuelve un int valor de 0...RAND_MAX. Entonces, si agrega dos de ellos, obtendrá hasta RAND_MAX * 2. si eso excede INT_MAXel resultado de la suma desborda el rango válido y int puede aguantar. El desbordamiento de valores firmados es un comportamiento indefinido y puede hacer que su teclado le hable en lenguas extranjeras.

Como no hay ganancia aquí al agregar dos resultados aleatorios, la idea simple es simplemente no hacerlo. Alternativamente, puede convertir cada resultado en unsigned int antes de la adición si eso puede contener la suma. O utilice un tipo más grande. Tenga en cuenta que long no es necesariamente más ancho que intlo mismo se aplica a long long si int es al menos 64 bits!

Conclusión: solo evita la adición. No proporciona más “aleatoriedad”. Si necesita más bits, puede concatenar los valores sum = a + b * (RAND_MAX + 1)pero eso también probablemente requiere un tipo de datos más grande que int.

Como su razón declarada es evitar un resultado cero: eso no se puede evitar sumando los resultados de dos rand() llamadas, ya que ambos pueden ser cero. En su lugar, puede simplemente incrementar. Si RAND_MAX == INT_MAXesto no se puede hacer en int. Sin embargo, (unsigned int)rand() + 1 lo hará muy, muy probablemente. Probablemente (no definitivamente), porque requiere UINT_MAX > INT_MAXlo cual es cierto en todas las implementaciones que conozco (que cubre bastantes arquitecturas integradas, DSP y todas las plataformas de escritorio, móviles y de servidor de los últimos 30 años).

Advertencia:

Aunque ya está rociado en los comentarios aquí, tenga en cuenta que agregar dos valores aleatorios no no obtener una distribución uniforme, pero una distribución triangular como tirar dos dados: para obtener 12 (dos dados) ambos dados tienen que mostrar 6. por 11 ya hay dos variantes posibles: 6 + 5 o 5 + 6etc.

Entonces, la adición también es mala desde este aspecto.

También tenga en cuenta que los resultados rand() genera no son independientes entre sí, ya que son generados por un generador de números pseudoaleatorios. Tenga en cuenta también que la norma no especifica la calidad o la distribución uniforme de los valores calculados.

  • @badmad: ¿Y qué pasa si ambas llamadas devuelven 0?

    – demasiado honesto para este sitio

    13 de junio de 2015 a las 18:08


  • @badmad: Me pregunto si UINT_MAX > INT_MAX != false está garantizado por la norma. (Suena probable, pero no estoy seguro si es necesario). Si es así, puede lanzar un solo resultado e incrementar (¡en ese orden!).

    – demasiado honesto para este sitio

    13 de junio de 2015 a las 18:16

  • Se gana al agregar múltiples números aleatorios cuando desea una distribución no uniforme: stackoverflow.com/questions/30492259/…

    – Corazón

    16 de junio de 2015 a las 0:09

  • para evitar 0, un simple “mientras que el resultado es 0, vuelva a tirar”?

    –Olivier Dulac

    16 de junio de 2015 a las 6:32

  • Agregarlos no solo es una mala manera de evitar 0, sino que también da como resultado una distribución no uniforme. Obtienes una distribución como los resultados de tirar los dados: 7 es 6 veces más probable que 2 o 12.

    – Barmar

    16/06/2015 a las 20:30

avatar de usuario
david hamen

Esta es una respuesta a una aclaración de la pregunta hecha en comentario a esta respuesta,

la razón por la que estaba agregando era evitar ‘0’ como el número aleatorio en mi código. rand()+rand() fue la solución rápida y sucia que me vino a la mente.

El problema era evitar el 0. Hay (al menos) dos problemas con la solución propuesta. Una es, como indican las otras respuestas, que rand()+rand() puede invocar un comportamiento indefinido. El mejor consejo es nunca invocar un comportamiento indefinido. Otro problema es que no hay garantía de que rand() no producirá 0 dos veces seguidas.

Lo siguiente rechaza cero, evita un comportamiento indefinido y, en la gran mayoría de los casos, será más rápido que dos llamadas a rand():

int rnum;
for (rnum = rand(); rnum == 0; rnum = rand()) {}
// or do rnum = rand(); while (rnum == 0);

  • Qué pasa rand() + 1?

    – preguntar a victor

    14 de junio de 2015 a las 9:35

  • @askvictor Eso podría desbordarse (aunque es poco probable).

    – Gerrit

    14 de junio de 2015 a las 10:25

  • @gerrit – depende de MAX_INT y RAND_MAX

    – preguntar a victor

    14 de junio de 2015 a las 10:26

  • @gerrit, me sorprendería que lo fueran no lo mismo, pero supongo que este es un lugar para pedantes 🙂

    – preguntar a victor

    14 de junio de 2015 a las 10:31

  • Si RAND_MAX==MAX_INT, rand() + 1 se desbordará exactamente con la misma probabilidad que el valor de rand() siendo 0, lo que hace que esta solución sea completamente inútil. Si está dispuesto a arriesgarse e ignorar la posibilidad de un desbordamiento, también puede usar rand() tal como está e ignorar la posibilidad de que devuelva 0.

    – Emil Jeřábek

    14 de junio de 2015 a las 16:47

avatar de usuario
Khaled.K

Básicamente rand() producir números entre 0 y RAND_MAXy 2 RAND_MAX > INT_MAX en tu caso.

Puede modular con el valor máximo de su tipo de datos para evitar el desbordamiento. Por supuesto, esto interrumpirá la distribución de los números aleatorios, pero rand es solo una forma de obtener números aleatorios rápidos.

#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;
}

Es posible que pueda intentar un enfoque bastante complicado asegurándose de que el valor devuelto por la suma de 2 rand() nunca exceda el valor de RAND_MAX. Un posible enfoque podría ser sum = rand()/2 + rand()/2; Esto aseguraría que para un compilador de 16 bits con un valor RAND_MAX de 32767, incluso si ambos rand devuelven 32767, incluso entonces (32767/2 = 16383) 16383+16383 = 32766, por lo tanto, no daría como resultado una suma negativa.

  • El OP quería excluir 0 de los resultados. La suma tampoco proporciona una distribución uniforme de valores aleatorios.

    – demasiado honesto para este sitio

    16 junio 2015 a las 23:20

  • @Olaf: No hay garantía de que dos llamadas consecutivas a rand() ambos no producirán cero, por lo que el deseo de evitar el cero no es una buena razón para sumar dos valores. Por otro lado, el deseo de tener una distribución no uniforme sería una buena razón para agregar dos valores aleatorios si uno garantiza que no se produzca un desbordamiento.

    – Super gato

    13 de junio de 2018 a las 16:53

avatar de usuario
kevin fegan

la razón por la que estaba agregando era evitar ‘0’ como el número aleatorio en mi código. rand()+rand() fue la solución rápida y sucia que me vino a la mente.

Una solución simple (bueno, llámalo “Hack”) que nunca produce un resultado cero y nunca se desbordará es:

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

Esto limitará su valor máximo, pero si no le importa eso, entonces debería funcionar bien para usted.

  • El OP quería excluir 0 de los resultados. La suma tampoco proporciona una distribución uniforme de valores aleatorios.

    – demasiado honesto para este sitio

    16 junio 2015 a las 23:20

  • @Olaf: No hay garantía de que dos llamadas consecutivas a rand() ambos no producirán cero, por lo que el deseo de evitar el cero no es una buena razón para sumar dos valores. Por otro lado, el deseo de tener una distribución no uniforme sería una buena razón para agregar dos valores aleatorios si uno garantiza que no se produzca un desbordamiento.

    – Super gato

    13 de junio de 2018 a las 16:53

avatar de usuario
eduardo karak

Para evitar 0, intente esto:

int rnumb = rand()%(INT_MAX-1)+1;

necesitas incluir limits.h.

  • Eso duplicará la probabilidad de obtener 1. Es básicamente lo mismo (pero posiblemente más lento) que agregar condicionalmente 1 si rand() rinde 0.

    – demasiado honesto para este sitio

    16 de junio de 2015 a las 23:18

  • Sí, tienes razón Olaf. Si rand() = 0 o INT_MAX -1, el rnumb será 1.

    – Doni

    17 de junio de 2015 a las 18:47

  • Peor aún, ahora que lo pienso. De hecho, duplicará la probabilidad de 1 y 2 (todo asumido RAND_MAX == INT_MAX). me olvidé de la - 1.

    – demasiado honesto para este sitio

    17 de junio de 2015 a las 19:01


  • los -1 aquí no sirve de nada. rand()%INT_MAX+1; todavía solo generaría valores en el [1…INT_MAX] rango.

    – chux – Reincorporar a Monica

    16 de febrero de 2018 a las 22:56

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad