Losuj listę <T>

852

Jaki jest najlepszy sposób na losowe uporządkowanie ogólnej listy w C #? Mam skończony zestaw 75 liczb na liście, do której chciałbym przypisać losową kolejność, aby narysować je dla aplikacji typu loterii.

Mirezus
źródło
3
Istnieje otwarty problem z integracją tej funkcji z .NET: github.com/dotnet/corefx/issues/461
Natan
5
Może Cię zainteresować ten pakiet NuGet , który zawiera metody rozszerzenia dla tasowania IList <T> i IEnumerable <T> przy użyciu algorytmu Fisher-Yates wspomnianego poniżej
ChaseMedallion
3
@Natan rozwiązali problem, ponieważ ktoś „pracował nad wieloma projektami i opracował wiele bibliotek i nigdy nie potrzebował takiej metody”, co mnie wkurzyło. Teraz musimy sami się zbadać, poszukać najlepszych wdrożeń, zmarnować czas, aby po prostu wynaleźć koło.
Vitalii Isaenko
1
Czy widzę to dobrze? Nie ma jednej poprawnej odpowiedzi funkcjonalnej po 10 latach? Być może potrzebujemy kolejnej nagrody za rozwiązanie, które dotyczy ilości potrzebnej entropii, aby przetasować listę z 75 liczbami $ log2 (75!) = 364 $ i jak to uzyskać. Trzeba będzie zresetować nawet kryptograficznie bezpieczny RNG z 256 bitami entropii przynajmniej raz podczas losowania rybaków.
Falco,
1
A jeśli zwykły programista nie jest w stanie rozwiązać tego problemu, czy wszyscy gramy w te same 0,01% możliwych gier w pasjansa na zawsze?
Falco

Odpowiedzi:

1135

Przetasuj dowolne (I)Listza pomocą metody rozszerzenia opartej na tasowaniu Fisher-Yates :

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)  
{  
    int n = list.Count;  
    while (n > 1) {  
        n--;  
        int k = rng.Next(n + 1);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}

Stosowanie:

List<Product> products = GetProducts();
products.Shuffle();

Powyższy kod używa bardzo krytykowanej metody System.Random do wybierania kandydatów do zamiany. Jest szybki, ale nie tak losowy, jak powinien. Jeśli potrzebujesz lepszej jakości losowości w tasowaniu, skorzystaj z generatora liczb losowych w System.Security.Cryptography:

using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
    RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
    int n = list.Count;
    while (n > 1)
    {
        byte[] box = new byte[1];
        do provider.GetBytes(box);
        while (!(box[0] < n * (Byte.MaxValue / n)));
        int k = (box[0] % n);
        n--;
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
    }
}

Proste porównanie jest dostępne na tym blogu (WayBack Machine).

Edycja: Od czasu napisania tej odpowiedzi kilka lat temu wiele osób skomentowało lub napisało do mnie, aby zwrócić uwagę na wielką głupią wadę mojego porównania. Mają oczywiście rację. Nie ma nic złego w System.Random, jeśli jest używany zgodnie z przeznaczeniem. W moim pierwszym przykładzie powyżej utworzyłem zmienną rng w metodzie Shuffle, która prosi o kłopoty, jeśli metoda będzie wywoływana wielokrotnie. Poniżej znajduje się stały, pełny przykład oparty na naprawdę przydatnym komentarzu otrzymanym dziś z @weston tutaj na SO.

Program.cs:

using System;
using System.Collections.Generic;
using System.Threading;

namespace SimpleLottery
{
  class Program
  {
    private static void Main(string[] args)
    {
      var numbers = new List<int>(Enumerable.Range(1, 75));
      numbers.Shuffle();
      Console.WriteLine("The winning numbers are: {0}", string.Join(",  ", numbers.GetRange(0, 5)));
    }
  }

  public static class ThreadSafeRandom
  {
      [ThreadStatic] private static Random Local;

      public static Random ThisThreadsRandom
      {
          get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
      }
  }

  static class MyExtensions
  {
    public static void Shuffle<T>(this IList<T> list)
    {
      int n = list.Count;
      while (n > 1)
      {
        n--;
        int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
        T value = list[k];
        list[k] = list[n];
        list[n] = value;
      }
    }
  }
}
granat
źródło
31
Co jeśli list.Count to> Byte.MaxValue? Jeśli n = 1000, to 255/1000 = 0, więc pętla do będzie nieskończoną pętlą, ponieważ pole [0] <0 jest zawsze fałszywe.
AndrewS,
18
Chciałbym zauważyć, że porównanie jest błędne. Problemem jest użycie <code> new Random () </code> w pętli, a nie losowość <code> Random </code> Objaśnienie
Sven
9
Dobrym pomysłem jest przekazanie instancji Random do metody losowej zamiast tworzenia jej w środku, tak jakbyś wywoływał losowo wiele razy w krótkich odstępach czasu (np. Losowanie wielu krótkich list), wszystkie listy będą tasowane w tym samym sposób (np. pierwszy element jest zawsze przenoszony na pozycję 3).
Mark Heath,
7
Tylko co rozwiązałoby problem w porównaniu postu. Ponieważ każde kolejne połączenie będzie następowało po poprzednich połączeniach ostatni losowy wynik. Random rng = new Random();static
weston
5
# 2, nie jest jasne, czy wersja z generatorem Crypto działa, ponieważ maksymalny zakres bajtu wynosi 255, więc każda lista większa niż ta nie będzie losowo odtwarzana.
Mark Sowul,
336

Jeśli musimy tylko tasować przedmioty w całkowicie losowej kolejności (po prostu mieszać przedmioty z listy), wolę ten prosty, ale skuteczny kod, który porządkuje przedmioty według przewodnika ...

var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();
użytkownik453230
źródło
40
Identyfikatory GUID mają być unikalne, a nie losowe. Część z nich oparta jest na maszynie, a inna na niepełnym wymiarze godzin i tylko niewielka część jest losowa. blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx
Despertar
99
To miłe, eleganckie rozwiązanie. Jeśli chcesz, aby coś innego niż przewodnik generowało losowość, po prostu zamów coś innego. Np .: var shuffledcards = cards.OrderBy(a => rng.Next()); compilr.com/grenade/sandbox/Program.cs
granat
20
Proszę nie. To jest źle. „losowe zamawianie” wcale NIE jest tasowaniem: wprowadzasz uprzedzenia, a co gorsza ryzykujesz przejście w nieskończoną pętlę
Vito De Tullio
77
@VitoDeTullio: Źle pamiętasz. Ryzykujesz nieskończone pętle, gdy udostępniasz funkcję losowego porównywania ; funkcja porównawcza jest wymagana do uzyskania spójnego zamówienia całkowitego . Losowy klucz jest w porządku. Ta sugestia jest błędna, ponieważ nie ma gwarancji, że prowadnice będą losowe , a nie dlatego, że technika sortowania według losowego klucza jest nieprawidłowa.
Eric Lippert,
24
@Doug: NewGuidgwarantuje tylko, że daje unikalny identyfikator GUID. Nie daje żadnych gwarancji losowości. Jeśli używasz identyfikatora GUID do celu innego niż tworzenie unikalnej wartości, robisz to źle.
Eric Lippert,
120

Jestem trochę zaskoczony wszystkimi niezdarnymi wersjami tego prostego algorytmu tutaj. Fisher-Yates (lub Knuth shuffle) jest nieco trudny, ale bardzo kompaktowy. Dlaczego to jest trudne? Ponieważ musisz zwrócić uwagę na to, czy generator liczb losowych r(a,b)zwraca wartość, gdzie bjest włączająca, czy wyłączna. Zredagowałem również opis Wikipedii, aby ludzie nie śledzili tam pseudokodu i nie tworzyli trudnych do wykrycia błędów. Dla .Net Random.Next(a,b)zwraca liczbę bbez niej, bez zbędnych ceregieli, oto jak można ją zaimplementować w C # /. Net:

public static void Shuffle<T>(this IList<T> list, Random rnd)
{
    for(var i=list.Count; i > 0; i--)
        list.Swap(0, rnd.Next(0, i));
}

public static void Swap<T>(this IList<T> list, int i, int j)
{
    var temp = list[i];
    list[i] = list[j];
    list[j] = temp;
}

Wypróbuj ten kod .

Shital Shah
źródło
Czy nie lepiej byłoby zmienić rnd (i, list.Count) na rnd (0, list.Count), aby dowolną kartę można było zamienić?
Pączki
15
@ Donuts - nie. Jeśli to zrobisz, dodasz stronniczość losowo.
Shital Shah,
2
Oddzielając Zamień <T> na osobną metodę, wydaje się, że powodujesz wiele niepotrzebnych przydziałów T dla temp.
Clay
2
Argumentowałbym, że LINQ może potencjalnie spowolnić wydajność przetasowania i byłby to powód, aby go nie używać, zwłaszcza biorąc pod uwagę względną prostotę kodu.
winglerw28
7
Kiedy i = list.Count - 1, tj. Ostatnia iteracja, rnd.Next(i, list.Count)oddam ci. Dlatego potrzebujesz i < list.Count -1jako warunek pętli. Cóż, nie potrzebujesz tego, ale oszczędza to 1 iterację;)
Pod
78

Metoda rozszerzenia dla IEnumerable:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy<T, int>((item) => rnd.Next());
}
Denis
źródło
3
Należy pamiętać, że to nie jest thread-safe, nawet jeśli są wykorzystywane na liście wątku bezpieczny
BlueRaja - Danny Pflughoeft
1
jak podajemy listę <ciąg> tej funkcji?
MonsterMMORPG
8
Z tym algorytmem wiążą się dwa istotne problemy: - OrderByużywa wariantu QuickSort do sortowania elementów według ich (pozornie losowych) kluczy. Wydajność QuickSort wynosi O (N log N) ; w przeciwieństwie do tego losowanie Fishera-Yatesa to O (N) . W przypadku kolekcji 75 elementów może to nie być wielka sprawa, ale różnica stanie się wyraźniejsza w przypadku większych kolekcji.
John Beyer
10
... - Random.Next()może generować rozsądnie pseudolosowy rozkład wartości, ale nie gwarantuje, że wartości będą unikalne. Prawdopodobieństwo zduplikowania kluczy rośnie (nieliniowo) z N, aż osiągnie pewność, że N osiągnie 2 ^ 32 + 1. OrderByQuickSort jest stabilny porządek; zatem jeśli wielu elementom zostanie przypisana ta sama wartość indeksu pseudolosowego, wówczas ich kolejność w sekwencji wyjściowej będzie taka sama jak w sekwencji wejściowej; w ten sposób do „tasowania” wprowadza się uprzedzenie.
John Beyer
27
@JohnBeyer: Istnieją znacznie, znacznie większe problemy niż to źródło uprzedzeń. Istnieje tylko cztery miliardy możliwych nasion dla Random, co jest znacznie, znacznie mniej niż liczba możliwych przetasowań zestawu o umiarkowanej wielkości. Można wygenerować tylko niewielką część możliwych przetasowań. To uprzedzenie przewyższa obciążenie wynikające z przypadkowych zderzeń.
Eric Lippert,
14

Pomysłem jest uzyskanie anonimowego obiektu z zamówieniem przedmiotu i losowym, a następnie zmiana kolejności przedmiotów według tego zamówienia i zwrócenie wartości:

var result = items.Select(x => new { value = x, order = rnd.Next() })
            .OrderBy(x => x.order).Select(x => x.value).ToList()
Andrey Kucher
źródło
2
najlepsze rozwiązanie jednego
linera
1
Brakuje średnika na końcu
fyi
Jeśli ktoś nie ma pewności co do rnd, dodaj to przed kodem powyżej Random rnd = new Random ();
Greg Trevellick
10
    public static List<T> Randomize<T>(List<T> list)
    {
        List<T> randomizedList = new List<T>();
        Random rnd = new Random();
        while (list.Count > 0)
        {
            int index = rnd.Next(0, list.Count); //pick a random item from the master list
            randomizedList.Add(list[index]); //place it at the end of the randomized list
            list.RemoveAt(index);
        }
        return randomizedList;
    }
Adam Tegen
źródło
4
Czy nie powinieneś zrobić czegoś takiego, var listCopy = list.ToList()aby uniknąć wyrzucenia wszystkich pozycji z listy przychodzących? Naprawdę nie rozumiem, dlaczego chcesz zmutować te listy, aby były puste.
Chris Marisic,
9

EDITRemoveAt jest słabość w mojej poprzedniej wersji. To rozwiązanie przezwycięża to.

public static IEnumerable<T> Shuffle<T>(
        this IEnumerable<T> source,
        Random generator = null)
{
    if (generator == null)
    {
        generator = new Random();
    }

    var elements = source.ToArray();
    for (var i = elements.Length - 1; i >= 0; i--)
    {
        var swapIndex = generator.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

Zwróć uwagę na opcjonalne Random generator, jeśli podstawowa implementacja frameworka Randomnie jest bezpieczna dla wątków lub kryptograficznie wystarczająco silna dla twoich potrzeb, możesz wstrzyknąć swoją implementację do operacji.

W tej odpowiedzi Randommożna znaleźć odpowiednią implementację bezpiecznej dla wątku implementacji kryptograficznie silnej .


Oto pomysł, rozszerz IList w (miejmy nadzieję) skuteczny sposób.

public static IEnumerable<T> Shuffle<T>(this IList<T> list)
{
    var choices = Enumerable.Range(0, list.Count).ToList();
    var rng = new Random();
    for(int n = choices.Count; n > 1; n--)
    {
        int k = rng.Next(n);
        yield return list[choices[k]];
        choices.RemoveAt(k);
    }

    yield return list[choices[0]];
}

Jodrell
źródło
Zobacz stackoverflow.com/questions/4412405/… . musisz już być świadomy.
nawfal
@nawfal zobacz moją ulepszoną implementację.
Jodrell
1
hmm, wystarczy. Czy to GetNextczy Next?
nawfal
4

Możesz to osiągnąć za pomocą tej prostej metody rozszerzenia

public static class IEnumerableExtensions
{

    public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target)
    {
        Random r = new Random();

        return target.OrderBy(x=>(r.Next()));
    }        
}

i możesz z niego skorzystać, wykonując następujące czynności

// use this on any collection that implements IEnumerable!
// List, Array, HashSet, Collection, etc

List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" };

foreach (string s in myList.Randomize())
{
    Console.WriteLine(s);
}
Shehab Fawzy
źródło
3
Chciałbym zachować Randominstancję klasy poza funkcją jako staticzmiennej. W przeciwnym razie możesz uzyskać ten sam materiał losowy z timera, jeśli zostanie wywołany w krótkim odstępie czasu.
Lemonseed
Interesująca uwaga - jeśli szybko utworzysz instancję klasy Random w pętli, powiedzmy między 0 ms a 200 ms dla siebie, masz bardzo dużą szansę na uzyskanie tego samego ziarna losowości - co następnie powoduje powtarzanie wyników. Możesz jednak obejść ten problem, używając Random rand = new Random (Guid.NewGuid (). GetHashCode ()); To skutecznie wymusza wyprowadzenie losowości z Guid.NewGuid ()
Baaleos
4

Jest to moja preferowana metoda odtwarzania losowego, gdy pożądane jest, aby nie modyfikować oryginału. Jest to wariant algorytmu „wywrotu” Fishera – Yatesa, który działa na dowolnej dowolnej sekwencji (długość sourcenie musi być znana od początku).

public static IList<T> NextList<T>(this Random r, IEnumerable<T> source)
{
  var list = new List<T>();
  foreach (var item in source)
  {
    var i = r.Next(list.Count + 1);
    if (i == list.Count)
    {
      list.Add(item);
    }
    else
    {
      var temp = list[i];
      list[i] = item;
      list.Add(temp);
    }
  }
  return list;
}

Ten algorytm można również zaimplementować, przydzielając zakres od 0dolength - 1 i losowo wyczerpując indeksy poprzez zamianę na losowo wybrany indeks z ostatniego indeksu aż wszystkie wskaźniki zostały wybrane dokładnie raz. Powyższy kod realizuje dokładnie to samo, ale bez dodatkowego przydziału. Co jest całkiem fajne.

Jeśli chodzi o Randomklasę, jest to generator liczb ogólnego przeznaczenia (a gdybym prowadził loterię, rozważyłbym użycie czegoś innego). Domyślnie opiera się on również na wartości początkowej opartej na czasie. Niewielkim złagodzeniem tego problemu jest zaszczepienie Randomklasy za pomocą RNGCryptoServiceProviderlub można użyć RNGCryptoServiceProvidermetody podobnej do tej (patrz poniżej), aby wygenerować jednolicie wybrane losowe podwójne zmiennoprzecinkowe wartości, ale prowadzenie loterii wymaga zrozumienia losowości i charakteru źródło losowości.

var bytes = new byte[8];
_secureRng.GetBytes(bytes);
var v = BitConverter.ToUInt64(bytes, 0);
return (double)v / ((double)ulong.MaxValue + 1);

Punktem generowania losowego podwójnego (wyłącznie od 0 do 1) jest zastosowanie do skalowania do rozwiązania liczby całkowitej. Jeśli chcesz wybrać coś z listy opartej na losowym podwójnym, xktóry zawsze będzie, 0 <= x && x < 1jest od razu.

return list[(int)(x * list.Count)];

Cieszyć się!

John Leidegren
źródło
4

Jeśli nie masz nic przeciwko użyciu dwóch Lists, jest to prawdopodobnie najłatwiejszy sposób na zrobienie tego, ale prawdopodobnie nie jest to najbardziej wydajny lub nieprzewidywalny:

List<int> xList = new List<int>() { 1, 2, 3, 4, 5 };
List<int> deck = new List<int>();

foreach (int xInt in xList)
    deck.Insert(random.Next(0, deck.Count + 1), xInt);
Xelights
źródło
3

Jeśli masz stałą liczbę (75), możesz utworzyć tablicę z 75 elementami, a następnie wyliczyć listę, przenosząc elementy do losowych pozycji w tablicy. Możesz wygenerować odwzorowanie numeru listy na indeks tablicy za pomocą losowania Fisher-Yates .

dmo
źródło
3

Zwykle używam:

var list = new List<T> ();
fillList (list);
var randomizedList = new List<T> ();
var rnd = new Random ();
while (list.Count != 0)
{
    var index = rnd.Next (0, list.Count);
    randomizedList.Add (list [index]);
    list.RemoveAt (index);
}
albertein
źródło
list.RemoveAt to operacja O (n), która powoduje, że implementacja jest zbyt powolna.
George Polevoy
1
    List<T> OriginalList = new List<T>();
    List<T> TempList = new List<T>();
    Random random = new Random();
    int length = OriginalList.Count;
    int TempIndex = 0;

    while (length > 0) {
        TempIndex = random.Next(0, length);  // get random value between 0 and original length
        TempList.Add(OriginalList[TempIndex]); // add to temp list
        OriginalList.RemoveAt(TempIndex); // remove from original list
        length = OriginalList.Count;  // get new list <T> length.
    }

    OriginalList = new List<T>();
    OriginalList = TempList; // copy all items from temp list to original list.
użytkownik7767814
źródło
0

Oto wydajny Shuffler, który zwraca tablicę bajtów przetasowanych wartości. Nigdy nie tasuje więcej niż jest to potrzebne. Można go zrestartować od miejsca, w którym poprzednio przerwał. Moja faktyczna implementacja (niepokazana) to komponent MEF, który umożliwia wymianę tasowania określoną przez użytkownika.

    public byte[] Shuffle(byte[] array, int start, int count)
    {
        int n = array.Length - start;
        byte[] shuffled = new byte[count];
        for(int i = 0; i < count; i++, start++)
        {
            int k = UniformRandomGenerator.Next(n--) + start;
            shuffled[i] = array[k];
            array[k] = array[start];
            array[start] = shuffled[i];
        }
        return shuffled;
    }

`

BSalita
źródło
0

Oto bezpieczny sposób, aby to zrobić:

public static class EnumerableExtension
{
    private static Random globalRng = new Random();

    [ThreadStatic]
    private static Random _rng;

    private static Random rng 
    {
        get
        {
            if (_rng == null)
            {
                int seed;
                lock (globalRng)
                {
                    seed = globalRng.Next();
                }
                _rng = new Random(seed);
             }
             return _rng;
         }
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
    {
        return items.OrderBy (i => rng.Next());
    }
}
Christopher Stevenson
źródło
0
 public Deck(IEnumerable<Card> initialCards) 
    {
    cards = new List<Card>(initialCards);
    public void Shuffle() 
     }
    {
        List<Card> NewCards = new List<Card>();
        while (cards.Count > 0) 
        {
            int CardToMove = random.Next(cards.Count);
            NewCards.Add(cards[CardToMove]);
            cards.RemoveAt(CardToMove);
        }
        cards = NewCards;
    }

public IEnumerable<string> GetCardNames() 

{
    string[] CardNames = new string[cards.Count];
    for (int i = 0; i < cards.Count; i++)
    CardNames[i] = cards[i].Name;
    return CardNames;
}

Deck deck1;
Deck deck2;
Random random = new Random();

public Form1() 
{

InitializeComponent();
ResetDeck(1);
ResetDeck(2);
RedrawDeck(1);
 RedrawDeck(2);

}



 private void ResetDeck(int deckNumber) 
    {
    if (deckNumber == 1) 
{
      int numberOfCards = random.Next(1, 11);
      deck1 = new Deck(new Card[] { });
      for (int i = 0; i < numberOfCards; i++)
           deck1.Add(new Card((Suits)random.Next(4),(Values)random.Next(1, 14)));
       deck1.Sort();
}


   else
    deck2 = new Deck();
 }

private void reset1_Click(object sender, EventArgs e) {
ResetDeck(1);
RedrawDeck(1);

}

private void shuffle1_Click(object sender, EventArgs e) 
{
    deck1.Shuffle();
    RedrawDeck(1);

}

private void moveToDeck1_Click(object sender, EventArgs e) 
{

    if (listBox2.SelectedIndex >= 0)
    if (deck2.Count > 0) {
    deck1.Add(deck2.Deal(listBox2.SelectedIndex));

}

    RedrawDeck(1);
    RedrawDeck(2);

}
sumit laddha
źródło
2
Witamy w Stack Overflow! Proszę rozważyć dodanie wyjaśnienia do swojej odpowiedzi, a nie tylko dużego bloku kodu. Naszym celem jest edukowanie ludzi, aby rozumieli odpowiedź i mogli ją zastosować w innych sytuacjach. Jeśli skomentujesz swój kod i dodasz wyjaśnienie, uczynisz swoją odpowiedź bardziej przydatną nie tylko dla osoby, która zadała pytanie tym razem, ale dla każdej osoby w przyszłości, która może mieć ten sam problem.
starsplusplus
4
Większość tego kodu jest całkowicie nieistotna dla pytania, a jedyna przydatna część w zasadzie powtarza odpowiedź Adama Tegena sprzed prawie 6 lat.
TC
0

Prosta modyfikacja zaakceptowanej odpowiedzi, która zwraca nową listę zamiast pracować w miejscu i akceptuje bardziej ogólne, IEnumerable<T>jak wiele innych metod Linq.

private static Random rng = new Random();

/// <summary>
/// Returns a new list where the elements are randomly shuffled.
/// Based on the Fisher-Yates shuffle, which has O(n) complexity.
/// </summary>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) {
    var source = list.ToList();
    int n = source.Count;
    var shuffled = new List<T>(n);
    shuffled.AddRange(source);
    while (n > 1) {
        n--;
        int k = rng.Next(n + 1);
        T value = shuffled[k];
        shuffled[k] = shuffled[n];
        shuffled[n] = value;
    }
    return shuffled;
}
Pozaziemskie
źródło
-5

Stary post na pewno, ale używam tylko GUID.

Items = Items.OrderBy(o => Guid.NewGuid().ToString()).ToList();

Identyfikator GUID jest zawsze unikalny i ponieważ jest generowany za każdym razem, gdy wynik zmienia się za każdym razem.

DavidMc
źródło
Kompaktowy, ale czy masz odniesienie do sortowania kolejnych nowychGuidów, aby były losowe wysokiej jakości? Niektóre wersje quid / uuid mają znaczniki czasu i inne nieprzypadkowe części.
Johan Lundberg,
8
Ta odpowiedź została już podana, a co gorsza, została zaprojektowana dla wyjątkowości, a nie przypadkowości.
Alex Angas,
-7

Bardzo prostym podejściem do tego rodzaju problemu jest użycie wielu losowych zamian elementów na liście.

W pseudo-kodzie wyglądałoby to tak:

do 
    r1 = randomPositionInList()
    r2 = randomPositionInList()
    swap elements at index r1 and index r2 
for a certain number of times
Aleris
źródło
1
Jednym z problemów związanych z tym podejściem jest wiedza, kiedy przestać. Ma również tendencję do wyolbrzymiania uprzedzeń w generatorze liczb pseudolosowych.
Mark Bessey,
3
Tak. Wysoce nieefektywny. Nie ma powodu, aby stosować takie podejście, gdy istnieją lepsze, szybsze podejścia, które są tak samo proste.
PeterAllenWebb
1
niezbyt wydajne lub skuteczne ... Uruchomienie go N razy prawdopodobnie pozostawiłoby wiele elementów w ich pierwotnej pozycji.
NSjonas,