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.
c#
generic-list
Mirezus
źródło
źródło
Odpowiedzi:
Przetasuj dowolne
(I)List
za pomocą metody rozszerzenia opartej na tasowaniu Fisher-Yates :Stosowanie:
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:
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:
źródło
Random rng = new Random();
static
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 ...
źródło
var shuffledcards = cards.OrderBy(a => rng.Next());
compilr.com/grenade/sandbox/Program.csNewGuid
gwarantuje 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.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ść, gdzieb
jest 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 .NetRandom.Next(a,b)
zwraca liczbęb
bez niej, bez zbędnych ceregieli, oto jak można ją zaimplementować w C # /. Net:Wypróbuj ten kod .
źródło
i = list.Count - 1
, tj. Ostatnia iteracja,rnd.Next(i, list.Count)
oddam ci. Dlatego potrzebujeszi < list.Count -1
jako warunek pętli. Cóż, nie potrzebujesz tego, ale oszczędza to 1 iterację;)Metoda rozszerzenia dla IEnumerable:
źródło
OrderBy
uż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.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.OrderBy
QuickSort 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.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:
źródło
źródło
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.EDIT
RemoveAt
jest słabość w mojej poprzedniej wersji. To rozwiązanie przezwycięża to.Zwróć uwagę na opcjonalne
Random generator
, jeśli podstawowa implementacja frameworkaRandom
nie jest bezpieczna dla wątków lub kryptograficznie wystarczająco silna dla twoich potrzeb, możesz wstrzyknąć swoją implementację do operacji.W tej odpowiedzi
Random
można znaleźć odpowiednią implementację bezpiecznej dla wątku implementacji kryptograficznie silnej .Oto pomysł, rozszerz IList w (miejmy nadzieję) skuteczny sposób.źródło
GetNext
czyNext
?Możesz to osiągnąć za pomocą tej prostej metody rozszerzenia
i możesz z niego skorzystać, wykonując następujące czynności
źródło
Random
instancję klasy poza funkcją jakostatic
zmiennej. W przeciwnym razie możesz uzyskać ten sam materiał losowy z timera, jeśli zostanie wywołany w krótkim odstępie czasu.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ść
source
nie musi być znana od początku).Ten algorytm można również zaimplementować, przydzielając zakres od
0
dolength - 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
Random
klasę, 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 zaszczepienieRandom
klasy za pomocąRNGCryptoServiceProvider
lub można użyćRNGCryptoServiceProvider
metody 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.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,
x
który zawsze będzie,0 <= x && x < 1
jest od razu.Cieszyć się!
źródło
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:źródło
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 .
źródło
Zwykle używam:
źródło
źródło
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.
`
źródło
Oto bezpieczny sposób, aby to zrobić:
źródło
źródło
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.źródło
Znalazłem interesujące rozwiązanie online.
Kurtuazja: https://improveandrepeat.com/2018/08/a-simple-way-to-shuffle-your-lists-in-c/
var shuffled = myList.OrderBy (x => Guid.NewGuid ()). ToList ();
źródło
Stary post na pewno, ale używam tylko GUID.
Identyfikator GUID jest zawsze unikalny i ponieważ jest generowany za każdym razem, gdy wynik zmienia się za każdym razem.
źródło
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:
źródło