Pertencente a rand() % n
ser inferior ao ideal
Doing rand() % n
tem uma distribuição não uniforme. Você receberá um número desproporcional de certos valores porque o número de valores não é múltiplo de 20
Em seguida, rand()
normalmente é um gerador congruencial linear (existem muitos outros , apenas esse é o mais provável implementado - e com parâmetros abaixo do ideal (existem várias maneiras de selecionar os parâmetros)). O maior problema com isso é que geralmente os bits baixos (os que você obtém com uma % 20
expressão de tipo) não são tão aleatórios. Lembro-me de um rand()
de anos atrás, onde o bit mais baixo alternava de 1
para 0
cada chamada para rand()
- não era muito aleatório.
Na página do manual rand (3):
As versões de rand () e srand () na Linux C Library usam o mesmo
gerador de números aleatórios como random () e srandom (), portanto, a ordem inferior
bits devem ser tão aleatórios quanto os bits de ordem superior. No entanto, em
implementações rand () e nas implementações atuais em diferentes
sistemas, os bits de ordem inferior são muito menos aleatórios do que os
encomendar bits. Não use esta função em aplicativos destinados a
portátil quando boa aleatoriedade é necessária.
Agora isso pode ser relegado à história, mas é bem possível que você ainda tenha uma implementação ruim do rand () oculta em algum lugar da pilha. Nesse caso, ainda é bastante aplicável.
A coisa a fazer é realmente usar uma boa biblioteca de números aleatórios (que fornece bons números aleatórios) e depois pedir números aleatórios dentro do intervalo desejado.
Um exemplo de um bom número de código aleatório (a partir das 13:00 no vídeo vinculado)
#include <iostream>
#include <random>
int main() {
std::mt19937 mt(1729); // yes, this is a fixed seed
std::uniform_int_distribution<int> dist(0, 99);
for (int i = 0; i < 10000; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
Compare isso com:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10000; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
}
Execute esses dois programas e compare com que frequência determinados números aparecem (ou não aparecem) nessa saída.
Vídeo relacionado: rand () considerado prejudicial
Alguns aspectos históricos do rand () causando bugs no Nethack que você deve observar e considerar em suas próprias implementações:
Problema do Nethack RNG
Rand () é uma função muito fundamental para a geração de números aleatórios do Nethack. O modo como o Nethack o usa é incorreto ou pode-se argumentar que lrand48 () produz números pseudo-aleatórios ruins. (No entanto, lrand48 () é uma função de biblioteca que usa um método PRNG definido e qualquer programa que o utilize deve levar em consideração os pontos fracos desse método.)
O problema é que o Nethack depende (às vezes exclusivamente como é o caso em rn (2)) nos bits mais baixos dos resultados de lrand48 (). Por esse motivo, o RNG em todo o jogo funciona mal. Isso é especialmente perceptível antes que as ações do usuário introduzam mais aleatoriedade, ou seja, na geração de personagens e na criação de primeiro nível.
Enquanto o anterior foi de 2003, ainda deve ser lembrado, pois pode não ser o caso de todos os sistemas que executam o jogo pretendido serem um sistema Linux atualizado com uma boa função rand ().
Se você está fazendo isso sozinho, pode testar o quão bom é o seu gerador de números aleatórios escrevendo algum código e testando a saída com ent .
Sobre as propriedades de números aleatórios
Existem outras interpretações de 'aleatório' que não são exatamente aleatórias. Em um fluxo aleatório de dados, é bem possível obter o mesmo número duas vezes. Se você jogar uma moeda (aleatória), é bem possível obter duas caras seguidas. Ou jogue um dado duas vezes e obtenha o mesmo número duas vezes seguidas. Ou girando uma roleta e obtendo o mesmo número duas vezes lá.
A distribuição de números
Ao reproduzir uma lista de músicas, as pessoas esperam que 'aleatório' signifique que a mesma música ou artista não será tocado pela segunda vez consecutiva. Jogar uma lista de reprodução The Beatles duas vezes seguidas é considerado 'não aleatório' (embora seja aleatório). A percepção de que para uma lista de reprodução de quatro músicas tocou um total de oito vezes:
1 3 2 4 1 2 4 3
é mais 'aleatório' do que:
1 3 3 2 1 4 4 2
Mais sobre isso para o 'embaralhar' de músicas: Como embaralhar as músicas?
Em valores repetidos
Se você não deseja repetir valores, há uma abordagem diferente que deve ser considerada. Gere todos os valores possíveis e embaralhe-os.
Se você está ligando rand()
(ou qualquer outro gerador de números aleatórios), está ligando para substituição. Você sempre pode obter o mesmo número duas vezes. Uma opção é descartar os valores repetidamente até selecionar um que atenda aos seus requisitos. Vou salientar que isso tem um tempo de execução não-determinístico e é possível que você se encontre em uma situação em que há um loop infinito, a menos que comece a fazer um rastreio mais complexo.
Lista e Escolha
Outra opção é gerar uma lista de todos os possíveis estados válidos e, em seguida, selecionar um elemento aleatório nessa lista. Encontre todos os pontos vazios (que atendem a algumas regras) na sala e escolha um aleatório nessa lista. E depois faça isso repetidamente até terminar.
Aleatório
A outra abordagem é embaralhar como se fosse um baralho de cartas. Comece com todos os pontos vazios da sala e comece a atribuí-los, distribuindo os pontos vazios, um de cada vez, para cada regra / processo solicitando um ponto vazio. Você termina quando fica sem cartas ou as coisas param de pedir por elas.