Observei que a rand()
função de biblioteca, quando é chamada apenas uma vez dentro de um loop, quase sempre produz números positivos.
for (i = 0; i < 100; i++) {
printf("%d\n", rand());
}
Mas quando adiciono duas rand()
chamadas, os números gerados agora têm mais números negativos.
for (i = 0; i < 100; i++) {
printf("%d = %d\n", rand(), (rand() + rand()));
}
Alguém pode explicar por que estou vendo números negativos no segundo caso?
PS: Inicializo a semente antes do loop como srand(time(NULL))
.
rand()
não pode ser negativo ...RAND_MAX
para o seu compilador? Você geralmente pode encontrá-lostdlib.h
. (Engraçado: verificaçãoman 3 rand
, ele tem a descrição de uma linha "mau gerador de números aleatórios".)abs(rand()+rand())
. Eu prefiro ter um UB positivo do que negativo! ;)Respostas:
rand()
é definido para retornar um número inteiro entre0
eRAND_MAX
.poderia transbordar. O que você observa é provavelmente o resultado de um comportamento indefinido causado pelo excesso de números inteiros.
fonte
a + b > a
se souberem dissob > 0
. Eles também podem assumir que, se houver uma declaração executada posteriormentea + 5
, o valor atual será menorINT_MAX - 5
. Portanto, mesmo no programa processador / interpretador de complemento 2 sem traps, pode não se comportar como seint
s fosse o complemento 2 sem traps.O problema é a adição.
rand()
retorna umint
valor de0...RAND_MAX
. Então, se você adicionar dois deles, você fará o que quiserRAND_MAX * 2
. Se isso excederINT_MAX
, o resultado da adição excederá o intervalo válido queint
pode ser mantido. O excesso de valores assinados é um comportamento indefinido e pode levar o teclado a falar com você em línguas estrangeiras.Como não há ganho aqui em adicionar dois resultados aleatórios, a idéia simples é simplesmente não fazê-lo. Como alternativa, você pode converter cada resultado
unsigned int
antes da adição, se isso puder conter a soma. Ou use um tipo maior. Observe quelong
não é necessariamente maior queint
, o mesmo se aplica along long
seint
tiver pelo menos 64 bits!Conclusão: Apenas evite a adição. Não fornece mais "aleatoriedade". Se você precisar de mais bits, poderá concatenar os valores
sum = a + b * (RAND_MAX + 1)
, mas isso provavelmente também requer um tipo de dados maior queint
.Como o motivo declarado é evitar um resultado zero: isso não pode ser evitado adicionando os resultados de duas
rand()
chamadas, pois ambas podem ser zero. Em vez disso, você pode apenas incrementar. SeRAND_MAX == INT_MAX
, isso não pode ser feito emint
. No entanto,(unsigned int)rand() + 1
fará muito, muito provavelmente. Provavelmente (não definitivamente), porque exigeUINT_MAX > INT_MAX
, o que é verdade em todas as implementações que eu conheço (que abrange várias arquiteturas incorporadas, DSPs e todas as plataformas de desktop, dispositivos móveis e servidores dos últimos 30 anos).Aviso:
Embora já tenha sido polvilhado nos comentários aqui, observe que a adição de dois valores aleatórios não obtém uma distribuição uniforme, mas uma distribuição triangular como rolar dois dados: para obter
12
(dois dados), ambos os dados precisam ser mostrados6
. pois11
já existem duas variantes possíveis:6 + 5
ou5 + 6
, etc.Portanto, a adição também é ruim nesse aspecto.
Observe também que os resultados
rand()
gerados não são independentes um do outro, pois são gerados por um gerador de números pseudo - aleatórios . Observe também que o padrão não especifica a qualidade ou a distribuição uniforme dos valores calculados.fonte
UINT_MAX > INT_MAX != false
é garantido pelo padrão. (Parece provável, mas não tenho certeza se necessário). Nesse caso, você pode apenas transmitir um único resultado e incrementar (nessa ordem!).Esta é uma resposta a um esclarecimento da pergunta feita no comentário a esta resposta ,
O problema era evitar 0. Há (pelo menos) dois problemas com a solução proposta. Uma é, como as outras respostas indicam, que
rand()+rand()
pode invocar um comportamento indefinido. O melhor conselho é nunca chamar um comportamento indefinido. Outra questão é que não há garantia querand()
não produza 0 duas vezes seguidas.O seguinte rejeita zero, evita comportamento indefinido e, na grande maioria dos casos, será mais rápido que duas chamadas para
rand()
:fonte
rand() + 1
?Produza basicamente
rand()
números entre0
eRAND_MAX
, e2 RAND_MAX > INT_MAX
no seu caso.Você pode modular com o valor máximo do seu tipo de dados para evitar o estouro. Esse curso interromperá a distribuição dos números aleatórios, mas
rand
é apenas uma maneira de obter números aleatórios rápidos.fonte
Pode ser que você tente uma abordagem complicada, garantindo que o valor retornado pela soma de 2 rand () nunca exceda o valor de RAND_MAX. Uma abordagem possível poderia ser sum = rand () / 2 + rand () / 2; Isso garantiria que, para um compilador de 16 bits com o valor RAND_MAX de 32767, mesmo que ambos os rand retornassem 32767, mesmo assim (32767/2 = 16383) 16383 + 16383 = 32766, portanto, não resultaria em soma negativa.
fonte
rand()
não produzam zero, portanto, o desejo de evitar zero não é uma boa razão para adicionar dois valores. Por outro lado, o desejo de ter uma distribuição não uniforme seria uma boa razão para adicionar dois valores aleatórios, se alguém garantir que o estouro não ocorra.Uma solução simples (ok, chame de "Hack") que nunca produz um resultado zero e nunca transborda é:
Isso limitará seu valor máximo, mas se você não se importa com isso, isso deve funcionar bem para você.
fonte
rand()
sempre retorna um valor não negativo). No entanto, deixaria a otimização para o compilador aqui.rand
não será negativo, a mudança será mais eficiente que a divisão por um número inteiro 2. assinado. A divisão por2u
poderia funcionar, mas sex
for umint
pode resultar em avisos sobre a conversão implícita assinado./ 2
qualquer maneira (eu já vi isso mesmo para algo como-O0
, ou seja, sem otimizações solicitadas explicitamente). É possivelmente a otimização mais trivial e mais estabelecida do código C. Point é que a divisão está bem definida pelo padrão para todo o intervalo inteiro, não apenas valores não negativos. Novamente: deixe otimizações para o compilador, escreva o código correto e claro em primeiro lugar. Isso é ainda mais importante para iniciantes.rand()
direita por um ou dividir por do2u
que ao dividir por 2, mesmo ao usar-O3
. Pode-se dizer razoavelmente que é improvável que essa otimização seja importante, mas dizer "deixe essas otimizações para o compilador" implicaria que os compiladores provavelmente as executariam. Você conhece algum compilador que realmente irá?Para evitar 0, tente o seguinte:
Você precisa incluir
limits.h
.fonte
rand()
rendimentos 0.1
e2
(todos assumidosRAND_MAX == INT_MAX
). Eu esqueci o- 1
.-1
aqui não tem valor.rand()%INT_MAX+1;
ainda geraria apenas valores no intervalo [1 ... INT_MAX].Embora o que todo mundo tenha dito sobre o provável estouro possa muito bem ser a causa do negativo, mesmo quando você usa números inteiros não assinados. O verdadeiro problema é realmente usar a funcionalidade de hora / data como a semente. Se você realmente se familiarizou com essa funcionalidade, saberá exatamente por que digo isso. O que realmente faz é dar uma distância (tempo decorrido) desde uma determinada data / hora. Embora o uso da funcionalidade de data / hora como a semente de um rand () seja uma prática muito comum, realmente não é a melhor opção. Você deve procurar alternativas melhores, pois existem muitas teorias sobre o assunto e eu não poderia entrar em todas elas. Você adiciona a essa equação a possibilidade de transbordamento e essa abordagem estava condenada desde o início.
Aqueles que postaram o rand () + 1 estão usando a solução que mais usa para garantir que eles não obtenham um número negativo. Mas, essa abordagem também não é realmente a melhor.
A melhor coisa que você pode fazer é dedicar um tempo extra para escrever e usar o tratamento adequado de exceções, e adicionar apenas ao número rand () se e / ou quando você terminar com um resultado zero. E, para lidar com números negativos corretamente. A funcionalidade rand () não é perfeita e, portanto, precisa ser usada em conjunto com o tratamento de exceções para garantir que você obtenha o resultado desejado.
Dedicar tempo e esforço extras para investigar, estudar e implementar adequadamente a funcionalidade rand () vale bem o tempo e o esforço. Apenas meus dois centavos. Boa sorte em seus empreendimentos...
fonte
rand()
não especifica qual semente usar. O padrão o especifica para usar um gerador pseudo-aleatório, e não uma relação com nenhum momento. Também não afirma sobre a qualidade do gerador. O problema atual é claramente o excesso. Observe querand()+1
é usado para evitar0
;rand()
não retorna um valor negativo. Desculpe, mas você perdeu o ponto aqui. Não se trata da qualidade do PRNG. .../dev/random
e usar um bom PRNG posteriormente (não tenho certeza da qualidade darand()
glibc) ou continuar usando o dispositivo - arriscando o seu aplicativo bloquear, se não houver entropia suficiente disponível. Tentar obter sua entropia no aplicativo pode muito bem ser uma vulnerabilidade, pois isso é possivelmente mais fácil de atacar. E agora se trata de endurecer - não aqui