Como a semente de printf() para evitar a colisão em um grande número de máquinas?
Pergunta
Normalmente, a propagação de printf() é feita por:
srand(time(NULL));
No meu caso, eu uso de números aleatórios para gerar um identificador para o meu processo de cliente em tempo de execução na rede.O processo às vezes é reiniciado e gera um novo identificador.Como o número de clientes aumenta, há uma boa chance de que dois clientes de chamada srand(time(NULL))
dentro do mesmo segundo, o que cria dois idênticos identificadores, ou uma colisão, como visto pelo lado do servidor. Algumas pessoas sugeriram uma resolução mais fina:
srand((time.tv_sec * 1000) + (time.tv_usec / 1000));
Mas O problema aqui é que a semente irá se repetir a cada 24 dias, mais ou menos, e quando o número de máquinas é grande o suficiente, ainda há uma chance de colisão. Não há outra solução:
srand(time.tv_usec * time.tv_sec);
Mas isso parece problemático para mim, também, porque o módulo do produto (o maior de bits de excesso e obter abandonada), não está uniformemente distribuído dentro do intervalo de unsigned int
valor da semente.Por exemplo, para cada s, time.tv_usec == 0
leva a mesma semente.
Assim é que existe uma forma de semente de printf() no meu caso?
Editar:o cliente é executado em Linux, Windows, Android e iOS, por isso, /dev/random
ou /dev/urandom
não está sempre disponível.
P. S.Eu estou ciente de que o GUID/UUID abordagem, mas eu gostaria de saber se é possível apenas de semente de printf() corretamente neste caso.
Solução
Você tem dois domínios: clientes e processos.Portanto, você precisa de um identificador exclusivo para cada um.Os processos obviamente podem ser feitos com o processo de processo.Para clientes, sugiro usar o endereço MAC, que deve ser exclusivo para cada interface de rede.Acredito que todas as plataformas que você lista soquetes de suporte, portanto, o IOCTL Siocgifhwaddr pode ser suportado.
O único problema é que os endereços MAC são 48 bits e os PIDs são tipicamente 32 bits, então você tem que escolher os bits de entropia mais altos dos dois valores a serem usados para sua semente SRAND.Eu sugiro os 16 bits inferiores do PID e os 16 bits inferiores do endereço MAC.
Outras dicas
srand
e rand
simplesmente não é adequado se você tem um monte de processos ou threads que precisam ter pseudo-aleatoriedade que é independente entre eles.
Em sistemas POSIX você pode usar o rand48
família de funções como jrand48
que tenham conhecido o tamanho do estado.Se depender do processo, thread e máquina de independência, você deve usar bits mais significativos do processo de ID, ID de thread, endereço IP e tempo para inicializar o estado. jrand[48]
demora (e modifica) um estado de três short
, então ele deve ser relativamente simples, a semente destes com os diferentes quantities.
Todos, mas um dos sistemas que são POSIX, que deve trabalhar lá.O que seria adequado a um fallback para sistemas Windows, eu não sei.
Se dois geradores de números aleatórios nunca tiverem colisões, eles não são aleatórios.Seria como jogar 'Snap', mas nunca recebendo uma partida.
Então, o que você quer é pior, em vez de um melhor gerador de números aleatórios.Usar GUIDs é de fato uma abordagem que deve remover inteiramente o problema para você.
Mas, se você está feliz apenas reduzindo a chance de colisões, em vez de eliminá-los inteiramente, você poderia usar o endereço IP IP da máquina (ou número de série do processador, ou somesuch) como parte da semente.
Se você está escrevendo para a plataforma linux, use /dev/random
e /dev/urandom
para obter números aleatórios.Ao contrário de srand()
, /dev/random
e /dev/urandom
usa o ruído gerado a partir de periféricos de hardware para gerar números aleatórios. srand()
usa semente fornecido como argumento para gerar números aleatórios.
Eu acho que você estará interessado no papel "Números aleatórios paralelos: tão fácil quanto 1, 2, 3".
citando do resumo: "Todos os nossos PRNGs passam rigorosos testes estatísticos (incluindo o Bigcrush de Testu01) e produzem pelo menos 264 fluxos paralelos exclusivos de números aleatórios, cada um com o período 2128 ou mais"