Query LINQ ottimale per ottenere una sottocartella casuale – Shuffle

Si prega di suggerire un modo più semplice per ottenere una raccolta casualmente casuale del conteggio “n” da una collezione con elementi “N”. dove n <= N

Un’altra opzione è usare OrderBy e ordinare su un valore GUID, che puoi usare usando:

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

Ho fatto alcuni test empirici per convincermi che quanto sopra effettivamente genera una distribuzione casuale (che sembra fare). Puoi vedere i miei risultati su Tecniche per il riordinamento casuale di una matrice .

Oltre alla risposta di mquander e al commento di Dan Blanchard, ecco un metodo di estensione LINQ-friendly che esegue uno shuffle 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]; } } } 

Questo ha alcuni problemi con “casual bias” e sono sicuro che non è ottimale, questa è un’altra possibilità:

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

Mescola la raccolta in un ordine casuale e prendi i primi n elementi dal risultato.

Un po ‘meno casuale, ma efficiente:

 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); 

Scrivo questo metodo di sovrascrittura:

 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(); } 

Quindi, ho il mio elenco di fonti (tutti gli elementi)

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

Infine, chiamo “Randomize” e ottengo una sotto-collezione casuale di elementi, nel mio caso, 5 elementi:

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

Scusa per il brutto codice :-), ma

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);