Menu

Números aleatórios em C#: Random.Next, intervalos, sementes, embaralhar e Random seguro

Como gerar números aleatórios em C# com a classe Random: Next e o limite superior exclusivo, doubles aleatórios em um intervalo, sementes para resultados reproduzíveis, sortear um elemento, embaralhar uma lista, a armadilha do new Random dentro de um laço e números criptograficamente seguros.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

A classe System.Random gera números pseudoaleatórios: uma sequência determinística que parece aleatória, a partir de uma semente. Crie uma instância e chame os métodos dela:

Exemplo de saída:

1294270287
83
You rolled a 5
0.6060

Execute de novo e os números mudam. O limite superior é exclusivo: Next(1, 7) pode retornar 1, 2, 3, 4, 5 ou 6, nunca 7. Para um número entre 1 e 10, inclusive, escreva Next(1, 11). Passar um max menor que min lança uma ArgumentOutOfRangeException.

Números aleatórios em um intervalo

Next(min, max) cobre os inteiros. Para um double em um intervalo, escale NextDouble(); para um decimal como um preço, monte-o a partir de inteiros para que o resultado tenha a precisão que você quer:

Exemplo de saída:

23.6 C
72.42
heads
N
-3

rng.Next(100, 10000) / 100m sorteia um número inteiro de centavos e divide por um decimal, então o preço sempre tem exatamente duas casas decimais. Escalar um double daria valores como 72.4183.

Sementes: sequências reproduzíveis

Passar uma semente inteira torna a sequência repetível. Dois objetos Random com a mesma semente produzem exatamente os mesmos números, na mesma ordem:

Saída:

16 29 91 99 91
16 29 91 99 91
38

Sementes são o que você quer para testes, simulações que precisa executar de novo, replays em jogos e fases geradas proceduralmente ("semente do mundo 2026"). Registre a semente usada, e um resultado surpreendente pode ser reproduzido depois.

A Microsoft não promete que uma semente produza a mesma sequência em todas as versões do .NET. Não guarde a saída com semente como dado (por exemplo, gerar IDs de clientes a partir de uma semente fixa e esperar gerá-los de novo depois de uma atualização).

Sorteando um elemento

Um índice aleatório é Next(0, count), que é sempre um índice válido porque o limite superior é exclusivo:

Saída:

Winner 1 gets a sticker pack
Winner 2 gets a mug
Winner 3 gets a sticker pack
common     695
rare       260
legendary  45

O sorteio com pesos mapeia um número de 0 a 99 em faixas cujos tamanhos são os pesos: de 0 a 69 é comum, de 70 a 94 raro, de 95 a 99 lendário. Em 1.000 sorteios, as contagens ficam perto de 700, 250 e 50 (aqui 695, 260 e 45).

Embaralhando uma lista: Fisher-Yates

Para colocar uma lista em ordem aleatória, percorra a partir do fim e troque cada elemento com um elemento aleatório na mesma posição ou antes dela. Esse é o embaralhamento de Fisher-Yates; ele é rápido e toda ordem tem a mesma chance. Escrito como um método genérico, ele funciona com arrays e listas de qualquer tipo de elemento:

Saída:

5 A 8 6 4 7 2 3
4 2 1 3 5

Dois atalhos que você vai ver na internet são piores. list.OrderBy(x => rng.Next()) funciona, mas ordena, então é mais lento, e as chaves aleatórias podem colidir. Trocar com rng.Next(items.Count) em vez de rng.Next(i + 1) parece parecido, mas deixa algumas ordens mais prováveis que outras. No .NET 8 em diante, Random.Shared.Shuffle(array) faz um embaralhamento correto no próprio array, e rng.GetItems(choices, 5) sorteia cinco itens (com repetição).

A armadilha do new Random() dentro de um laço

Um bug clássico é criar um Random novo toda vez que um número é necessário:

// Don't do this
int RollDie()
{
    var rng = new Random();   // new instance on every call
    return rng.Next(1, 7);
}

No .NET Framework, new Random() sem semente usa o relógio do sistema (Environment.TickCount), que só muda a cada 10 a 16 milissegundos. Chamadas feitas em um laço rápido criam instâncias com a mesma semente e retornam o mesmo número, então um laço de rolagens de dado imprime 4 4 4 4 4. O .NET Core e o .NET 5+ dão a cada instância uma semente de uma fonte aleatória compartilhada, o que esconde o sintoma, mas criar um objeto por número continua sendo um desperdício, e o código quebra quando é reutilizado em projetos mais antigos ou na Unity.

A solução é uma instância para a classe, guardada em um campo:

Saída:

3 6 6 5 6 1 2 1

A semente aqui só existe para que o exemplo imprima a mesma coisa em toda execução; em um jogo real você escreveria new Random().

Threads e Random.Shared

Random não é thread safe. Se duas threads chamarem Next na mesma instância ao mesmo tempo, o estado interno dela pode se corromper; o sintoma conhecido é uma instância que passa a retornar 0 para sempre. As opções, da mais simples:

static class Dice
{
    // .NET 6 and later: a thread-safe shared instance
    public static int RollShared() => Random.Shared.Next(1, 7);

    // Any version: one instance per thread
    [ThreadStatic] private static Random _local;
    private static Random Local => _local ?? (_local = new Random(Guid.NewGuid().GetHashCode()));
    public static int RollPerThread() => Local.Next(1, 7);

    // Any version: a lock around one shared instance
    private static readonly Random _rng = new Random();
    private static readonly object _sync = new object();
    public static int RollLocked()
    {
        lock (_sync) { return _rng.Next(1, 7); }
    }
}

Random.Shared é o padrão certo em código novo: é thread safe, não precisa de campo e recebe uma semente aleatória. Ele não aceita semente, então use o seu próprio new Random(seed) quando precisar de reprodutibilidade.

Números aleatórios criptograficamente seguros

System.Random é previsível por projeto. Quem conhece a semente, ou vê saídas suficientes, consegue calcular o resto. Isso serve para jogos e amostragem, e é errado para senhas, tokens de redefinição, IDs de sessão, chaves e sorteios. Para esses casos, use RandomNumberGenerator, de System.Security.Cryptography, que lê do gerador seguro do sistema operacional:

Exemplo de saída:

Verification code: 276062
Reset token: 391e40ab5fa205e6457e48661586d10a
Password: gyv8LWyajJcF

RandomNumberGenerator.GetInt32 (disponível no .NET Core 3.0 em diante) retorna um inteiro sem viés dentro do intervalo. Não monte códigos seguros com bytes[0] % 10: o resto da divisão deixa alguns dígitos mais prováveis que outros. No .NET 6 em diante, RandomNumberGenerator.GetBytes(16) retorna um array novo diretamente, e Convert.ToHexString(bytes) o formata. Para identificadores únicos em que o sigilo não importa, Guid.NewGuid() é mais simples.

Perguntas frequentes

Como gerar um número aleatório em C#?

Crie um objeto Random e chame Next: var rng = new Random(); int roll = rng.Next(1, 7); dá de 1 a 6. Next(max) dá de 0 a max - 1, e NextDouble() dá um double de 0.0 até 1.0, sem incluir o 1.0. No .NET 6 em diante, você pode dispensar o objeto e usar Random.Shared.Next(1, 7).

O limite superior de Random.Next é inclusivo?

Não. Next(min, max) retorna um número maior ou igual a min e estritamente menor que max. Para um número de 1 a 10, inclusive, escreva Next(1, 11). Isso faz de Next(0, list.Count) um índice válido de uma lista.

Por que new Random() dá os mesmos números?

No .NET Framework, new Random() recebe a semente do relógio do sistema, que só muda a cada alguns milissegundos, então várias instâncias criadas em um laço rápido recebem a mesma semente e a mesma sequência. O .NET Core e o .NET 5+ dão uma semente diferente a cada instância, mas a solução é a mesma em todo lugar: crie um Random e reutilize-o.

Como obter sempre os mesmos números aleatórios em C#?

Passe uma semente ao construtor: new Random(42). Duas instâncias com a mesma semente produzem a mesma sequência, o que torna reproduzíveis testes, simulações e geração procedural. A sequência de uma semente pode mudar entre versões do .NET, então não guarde a saída com semente como dado permanente.

System.Random é seguro o bastante para senhas ou tokens?

Não. Random é previsível: quem descobrir o estado ou a semente consegue reproduzir os números. Para senhas, tokens, chaves e qualquer coisa ligada a segurança, use System.Security.Cryptography.RandomNumberGenerator, por exemplo RandomNumberGenerator.GetInt32(0, 10) ou RandomNumberGenerator.GetBytes(32).

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR