Consulta LINQ ideal para obter uma sub-coleção aleatória – Shuffle

Por favor, sugira uma maneira mais fácil de obter uma coleção aleatória de contagem ‘n’ de uma coleção com itens ‘N’. onde n <= N

Outra opção é usar OrderBy e classificar em um valor de GUID, que você pode fazer isso usando:

var result = sequence.OrderBy(elem => Guid.NewGuid()); 

Fiz alguns testes empíricos para me convencer de que o acima, na verdade, gera uma distribuição aleatória (o que parece fazer). Você pode ver meus resultados em Técnicas para reordenar aleatoriamente uma matriz .

Além da resposta de Mquander e do comentário de Dan Blanchard, aqui está um método de extensão amigável ao LINQ que executa um shuffle de Fisher-Yates-Durstenfeld :

 // take n random items from yourCollection var randomItems = yourCollection.Shuffle().Take(n); // ... public static class EnumerableExtensions { public static IEnumerable Shuffle(this IEnumerable source) { return source.Shuffle(new Random()); } public static IEnumerable Shuffle(this IEnumerable source, Random rng) { if (source == null) throw new ArgumentNullException("source"); if (rng == null) throw new ArgumentNullException("rng"); return source.ShuffleIterator(rng); } private static IEnumerable ShuffleIterator( this IEnumerable source, Random rng) { var buffer = source.ToList(); for (int i = 0; i < buffer.Count; i++) { int j = rng.Next(i, buffer.Count); yield return buffer[j]; buffer[j] = buffer[i]; } } } 

Isso tem alguns problemas com “viés random” e tenho certeza que não é o ideal, esta é outra possibilidade:

 var r = new Random(); l.OrderBy(x => r.NextDouble()).Take(n); 

Embaralhe a coleção em uma ordem aleatória e pegue os primeiros n itens do resultado.

Um pouco menos random, mas eficiente:

 var rnd = new Random(); var toSkip = list.Count()-n; if (toSkip > 0) toSkip = rnd.Next(toSkip); else toSkip=0; var randomlySelectedSequence = list.Skip(toSkip).Take(n); 

Eu escrevo este método de substituições:

 public static IEnumerable Randomize(this IEnumerable items) where T : class { int max = items.Count(); var secuencia = Enumerable.Range(1, max).OrderBy(n => n * n * (new Random()).Next()); return ListOrder(items, secuencia.ToArray()); } private static IEnumerable ListOrder(IEnumerable items, int[] secuencia) where T : class { List newList = new List(); int count = 0; foreach (var seed in count > 0 ? secuencia.Skip(1) : secuencia.Skip(0)) { newList.Add(items.ElementAt(seed - 1)); count++; } return newList.AsEnumerable(); } 

Então, eu tenho minha lista de fonts (todos os itens)

 var listSource = p.Session.QueryOver(() => pl) .Where(...); 

Finalmente, eu chamo de “Randomize” e recebo uma sub-coleção de itens aleatória, no meu caso, 5 itens:

 var SubCollection = Randomize(listSource.List()).Take(5).ToList(); 

Desculpe pelo código feio :-), mas

var result =yourCollection.OrderBy(p => (p.GetHashCode().ToString() + Guid.NewGuid().ToString()).GetHashCode()).Take(n);
var result =yourCollection.OrderBy(p => (p.GetHashCode().ToString() + Guid.NewGuid().ToString()).GetHashCode()).Take(n);