Gere números randoms distintos em c #

Só queria saber se você pode confirmar que o código a seguir é válido e avisa se há alternativas melhores para ele?

Eu estou tentando gerar uma coleção de números randoms distintos entre 1 e 100.000.

Random rand = new Random(); List result = new List(); for (Int32 i = 0; i  value == curValue)) { curValue = rand.Next(1, 100000); } result.Add(curValue); } 

Sim, até onde eu sei, o código faz exatamente o que você quer.

Looping através da lista para verificar cada valor não é muito eficiente. Você pode colocar os valores em um HashSet para acelerar o ckeck.

Como o ‘HashSet’ não preserva a ordem dos itens, você ainda precisa da List :

 Random rand = new Random(); List result = new List(); HashSet check = new HashSet(); for (Int32 i = 0; i < 300; i++) { int curValue = rand.Next(1, 100000); while (check.Contains(curValue)) { curValue = rand.Next(1, 100000); } result.Add(curValue); check.Add(curValue); } 

Não que isso importe muito se você precisar apenas de 300 números, mas seria mais eficiente usar o HashSet que faz o (1) lookups em vez de o (n) como na List.

 Random rand = new Random(); HashSet result = new HashSet(); while (result.Count < 300) { result.Add(rand.Next(1, 1000000)); } 

Reutilize o homem. Uma vez que você quer fazer é uma coleção de números (Enumerable.Range vai fazer), a segunda coisa é embaralhar (aleatoriamente) esses números. Você pode encontrar resposta para este problema no SO, e graças a solução LINQ é lindo, mas talvez não seja o mais rápido, de qualquer maneira eu uso, porque eu gosto da beleza dele.

Linq shuffling: http://www.code56.com/2009/02/lets-do-some-shuffling.html (link morto). Implementação muito semelhante: http://csharpsimple.blogspot.com/2012/01/shuffle-in-linq-part-2.html

Em resumo:

 var rand = new Random(); return source.Select(t => new { Index = rand.Next(), Value = t }) .OrderBy(p => p.Index) .Select(p => p.Value); 

Isso fará o que você quiser, exceto que jogar um número porque você o viu antes reduz a entropia (aleatoriedade) da sequência.

Sua versão funcionará, então o que estou sugerindo é apenas uma alternativa divertida:

 List result = new List(); Queue working = new Queue(new int[2] {1, 100000}); Random rand = new Random(); while (result.Count < 300) { int lower = working.Dequeue(); int upper = working.Dequeue(); int pivot = rand.Next(lower,upper); result.Add(pivot); if(lower < pivot) { working.Enqueue(lower); working.Enqueue(pivot); } if(pivot+1 < upper) { working.Enqueue(pivot + 1); working.Enqueue(upper); } } 

Nesses casos, sempre acho que você não precisa de números randoms . Em vez disso, você vai mais embaralhar alguns números existentes.

Então, que tal pegar o range que você gosta com algo como var range = Enumerable.Range(1, 100000) , embaralhe-os e pegue o número de elementos que você precisa, por algo como range.ToArray().Shuffle().Take(300) ?

Uma implementação de um algoritmo random pode ser encontrada aqui . Eu sei que não é um método de extensão, que pode ser usado da maneira descrita acima, mas alterar a assinatura é muito fácil.

Se você estiver disposto a fazer uma tentativa inicial para pré-gerar uma lista de 100.000 ints, esse método é muito rápido:

 static IList GetRandoms(int[] sample) { var rand = new Random(); var result = new int[300]; var count = sample.Length; for (int i = 0; i < 300; i++) { var index = rand.Next(count); result[i] = sample[index]; sample[index] = sample[--count]; } return result; } 

Então você chamaria assim:

 var sample = Enumerable.Range(1, 100000).ToArray(); var data = GetRandoms(sample); 

Mudou minha resposta desleixada!

 var result = new HashSet(); var random = new Random(); var seq = Enumerable.Range(1, 30).GetEnumerator(); while(seq.MoveNext()) { while(!result.Add(random.Next(1, 100000))); } 

Eu percebo que este é um post muito antigo, mas para adicionar na minha experiência recente de fazer algo ao longo das mesmas linhas (eu tive que gerar 100k Ids de documentos randoms com ID’s estrangeiros em que ambos tinham de ser listas exclusivas de #).

Para acelerar, você pode querer multithread-lo com um parallel.for – em caso afirmativo, você não pode usar um hashset. Em vez disso, use um byte onde será apenas ConcurrentDictionary de onde o byte será um ‘novo byte ()’

Levou o tempo de execução de ~ 1,5 horas para cerca de 20 minutos para mim. Se você precisar, sugiro usar um linkedList para armazenar também, em vez de uma fila ou lista, a menos que você tenha uma necessidade específica de access random.