Optimale LINQ-Abfrage eine zufällige Unter Sammlung zu erhalten - Shuffle
-
22-07-2019 - |
Frage
Bitte einen einfachste Weg vorschlagen, eine zufällige Shuffled Sammlung von count ‚n‘ aus einer Sammlung mit ‚N‘ Elementen zu erhalten. wobei n <= N
Lösung
Eine weitere Option ist SortiertNach zu verwenden und auf einem GUID-Wert zu sortieren, die Sie tun können, so verwenden:
var result = sequence.OrderBy(elem => Guid.NewGuid());
Ich habe einige empirische Versuche, mich zu überzeugen, dass die oben erzeugt tatsächlich eine zufällige Verteilung (die es zu tun scheint). Sie können meine Ergebnisse unter Techniken für Randomly Neuordnen ein Array .
Andere Tipps
Im Anschluss an mquander Antwort und Dan Blanchard Kommentar, hier ist eine LINQ freundliche Erweiterung Methode, die ein Fisher-Yates-Durstenfeld mischt :
// take n random items from yourCollection
var randomItems = yourCollection.Shuffle().Take(n);
// ...
public static class EnumerableExtensions
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
return source.Shuffle(new Random());
}
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
if (source == null) throw new ArgumentNullException("source");
if (rng == null) throw new ArgumentNullException("rng");
return source.ShuffleIterator(rng);
}
private static IEnumerable<T> ShuffleIterator<T>(
this IEnumerable<T> 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];
}
}
}
Dieses einige Probleme mit „random bias“ hat, und ich bin sicher, dass es nicht optimal ist, ist dies eine weitere Möglichkeit:
var r = new Random();
l.OrderBy(x => r.NextDouble()).Take(n);
Shuffle die Sammlung in zufälliger Reihenfolge und die erste n
Elemente aus dem Ergebnis.
Ein bisschen weniger zufällig, aber effizient:
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);
Ich schreibe dies überschreibt Methode:
public static IEnumerable<T> Randomize<T>(this IEnumerable<T> items) where T : class
{
int max = items.Count();
var secuencia = Enumerable.Range(1, max).OrderBy(n => n * n * (new Random()).Next());
return ListOrder<T>(items, secuencia.ToArray());
}
private static IEnumerable<T> ListOrder<T>(IEnumerable<T> items, int[] secuencia) where T : class
{
List<T> newList = new List<T>();
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<T>();
}
Dann habe ich meine Quellenliste (alle Artikel)
var listSource = p.Session.QueryOver<Listado>(() => pl)
.Where(...);
Schließlich nenne ich „Randomize“ und ich bekomme eine zufällige Unter Sammlung von Gegenständen, in meinem Fall 5 Artikel:
var SubCollection = Randomize(listSource.List()).Take(5).ToList();
Es tut uns hässlich Code :-), aber
var result =yourCollection.OrderBy(p => (p.GetHashCode().ToString() + Guid.NewGuid().ToString()).GetHashCode()).Take(n);