Estou criando um conjunto de dados pseudo-aleatórios para os usuários do aplicativo treinarem.
Estou surpreso que se eu semear a função RAND() com 1, 2, 3, etc., recebo quase o mesmo resultado da função propagada. No entanto, isso parece ser seguido por valores "apropriadamente aleatórios", mas repetíveis, quando uma semente não é fornecida.
SELECT RAND(1) AS R1A, RAND() AS R1B, RAND(2) AS R2A, RAND() AS R2B,
RAND(3) AS R3A, RAND() AS R3B, RAND(4) AS R4A, RAND() AS R4B
0.713591993212924
0.472241415009636
0.713610626184182
0.217821139260039
0.71362925915544
0.963400850719992
0.713647892126698
0.708980575436056
À primeira vista, parece que posso avaliar RAND(@seed) e descartar o resultado, depois avaliar RAND() para obter vários números realmente "aleatórios" para meus dados de treinamento - até agora planejei usar quatro por registro; Eu posso precisar de um pouco mais.
Esse plano vai funcionar corretamente? E, o que estou olhando, aqui? E, deveria estar na documentação? Eu não encontrei.
A documentação diz isso, o que pode ser uma pista:
A função RAND é um gerador de números pseudoaleatórios que opera de maneira semelhante à função rand da biblioteca de tempo de execução C. Se nenhuma semente for fornecida, o sistema gerará seus próprios números de semente variáveis.
A função rand em C produz saída semelhante para entrada de semente semelhante?
Eu acho que a documentação também poderia declarar mais claramente que RAND(@number) seguido por RAND() sempre gera os mesmos números. Mas era isso que eu queria e o que qualquer programador de computador experiente espera.
Suponho que poderia preencher uma tabela com chaves de dados aleatórias obtidas em https://www.random.org/ para usar com esse propósito - mas isso tem desvantagens.
Atualização, conclusão provisória
Tenho as seguintes conclusões sobre RAND() e por enquanto acho que vou continuar com isso, mas mantendo as alternativas em mente.
RAND(@int) define a semente do gerador de números aleatórios usando o valor inteiro fornecido e retorna um resultado float que não é estatisticamente independente, visto que RAND(@int) e RAND(@int+1) produzem quase exatamente o mesmo resultado.
Claro, RAND(@int) sempre produz o mesmo resultado.
RAND(-@int) e RAND(@int) produzem o mesmo resultado.
RAND(0) é uma exceção: pode haver outras exceções. RAND(0) sempre produz o resultado 0,943597390424144, mas não é semelhante ao resultado de RAND(1).
RAND() chamado n vezes depois de RAND(@int) sempre produz os mesmos n números. Se chamarmos o enésimo número "rand(@int, @n)" -
CREATE PROCEDURE sproc_rand(@seed int, @nth int, @rand float OUTPUT) AS
SET @rand = RAND(@seed);
WHILE ( @nth > 0 ) BEGIN SET @rand = RAND(); SET @nth = @nth - 1; END
A diferença entre rand(@int, @n) e rand(@int+1, @n) "módulo 1" -
(1.0 + rand(@int, @n) - rand(@int+1, @n) % 1
É uma constante ou quase constante; para @n = 1 é aproximadamente 0,75. Para @n = 5 é 0,991. Para @n = 6 é 0,91. Para @n = 100 é 0,83.
Portanto, não, esses não são bons números "aleatórios" quando uma semente incrementada simples é usada - embora eles saltem muito bem para @n IN (1, 2, 3, 4).
O que estou considerando agora como mitigação é não usar RAND(row_id), mas,
RAND(row_id * @factor_1 + @factor_2)
onde @factor1 e @factor2 são termos constantes e @factor_1 é aproximadamente 10.000. E para projetos diferentes, fatores diferentes.
Até agora, se eu quiser resultados repetíveis, o outro método disponível (além do Inverso Multiplicativo, que eu ainda não entendi) é criar uma tabela e preenchê-la com números aleatórios não repetidos de um dos outros métodos, em seguida, desenhe os números dessa tabela, quando necessário.
A única razão para fornecer uma "semente"
RAND()
é se você deseja exatamente a mesma sequência de (pseudo) valores aleatórios para fins de teste.Basta chamá-lo sem uma semente, e isso deve ser bom o suficiente para a maioria dos propósitos.
Se você precisa de uma sequência previsível e está simplesmente insatisfeito com o valor inicial (aleatório),
RAND()
leva sementes de -2.147.483.648 até 2.147.483.647 (intervalo completo de int), então tente algumas sementes que diferem em mais de 1 ou 2.Por fim, se você estivesse no SQL 2008 ou melhor , também teria a opção de considerar CRYPT_GEN_RANDOM (aviso, ele retorna um varbinary, não um float, então você provavelmente precisaria alterar um pouco seu código): veja isto artigo para algumas diferenças e discussão .
Sim, chamar
RAND()
com uma semente produz o mesmo resultado em todas/a maioria das versões do SQL Server. Testei no SQL Server 2012 e obtive os mesmos resultados que está na pergunta. Além disso, se você executar o seguinte, verá que qualquer número deRAND()
sem semente produzirá o mesmo resultado todas as vezes se houver pelo menos umRAND(<seed>)
no grupo, mesmo que estejam em lotes separados:No entanto, se você estiver procurando por números pseudo-aleatórios que são:
então você precisa dar uma olhada usando Modular Multiplicative Inverses (MMI). Eu explico isso com mais detalhes em uma resposta à seguinte pergunta do StackOverflow:
Gere um tempo aleatório diferente no intervalo determinado
Esta técnica também é muito rápida e não requer pré-geração (ou seja, nenhum armazenamento do conjunto aleatório). Ele também funciona em versões anteriores do SQL Server – 2000 e 2005 – onde
CRYPT_GEN_RANDOM
não está disponível.Também tenho uma apresentação que fiz sobre esse tópico que inclui UDFs T-SQL genéricas para gerar os valores. Confira a apresentação Efficiently Generate Unique, Pseudo-Random Numbers disponível em:
http://www.SqlQuantumLeap.com/presentations/
Eu posso criar uma aleatoriedade muito boa com técnica. Se você precisar de valores reais, tente criar uma tabela tinyint etc. Obviamente, não é ideal para tamanhos de amostra grandes.
SELECT TOP 1 [Value] FROM [TinyInt] ORDER BY NEWID();
Eu não o uso apenas para números aleatórios. Eu o uso para qualquer coisa que possa não terminar a tempo, então quero ter certeza de obter tudo em algumas execuções em lote. Funciona muito bem para manutenção.