Mam taką tablicę:
var arr1 = ["a", "b", "c", "d"];
Jak mogę go losowo / losowo przetasować?
javascript
arrays
random
shuffle
Kliknij opcję Upvote
źródło
źródło
Odpowiedzi:
De facto algorytm tasowania bezstronnego to tasowanie Fishera-Yatesa (inaczej Knuth).
Zobacz https://github.com/coolaj86/knuth-shuffle
Tutaj możesz zobaczyć świetną wizualizację (oraz link do oryginalnego postu )
Więcej informacji na temat zastosowanego algorytmu .
źródło
i--
nie--i
. Ponadto, testif (i==0)...
jest zbędny, ponieważ jeśli natomiast pętla nigdy nie zostanie wprowadzona. Wezwanie do można wykonać szybciej za pomocą . Albo Tempa lub tempj można usunąć, a wartość należy przypisać do myArray [I] lub J , odpowiednio.i == 0
Math.floor
...| 0
0 !== currentIndex
).Oto implementacja JavaScript shuffle Durstenfelda , zoptymalizowanej wersji Fisher-Yatesa:
Wybiera losowy element dla każdego oryginalnego elementu tablicy i wyklucza go z następnego losowania, jak losowe wybieranie z talii kart.
To sprytne wykluczenie zamienia wybrany element z bieżącym, a następnie wybiera następny losowy element z reszty, zapętlając do tyłu w celu uzyskania optymalnej wydajności, zapewniając, że losowy wybór jest uproszczony (zawsze można rozpocząć od 0), a tym samym pomija ostatni element.
Czas działania algorytmu to
O(n)
. Pamiętaj, że losowanie odbywa się w miejscu, więc jeśli nie chcesz modyfikować oryginalnej tablicy, najpierw wykonaj jej kopię.slice(0)
.EDYCJA: Aktualizacja do ES6 / ECMAScript 2015
Nowy ES6 pozwala nam przypisać dwie zmienne jednocześnie. Jest to szczególnie przydatne, gdy chcemy zamienić wartości dwóch zmiennych, ponieważ możemy to zrobić w jednym wierszu kodu. Oto krótsza forma tej samej funkcji, korzystająca z tej funkcji.
źródło
return array
ponieważ JavaScript przekazuje tablice przez referencję, gdy jest używany jako argument funkcji. Zakładam, że pozwala to zaoszczędzić miejsce na stosie, ale jest to interesująca mała funkcja. Wykonanie losowania na tablicy spowoduje przetasowanie oryginalnej tablicy.Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Zobacz Generowanie losowych liczb całkowitych w JavaScript w określonym zakresie? dla bardzo wyczerpującego wyjaśnienia.źródło
Można (lub należy) użyć go jako protoype z Array:
Od ChristopheD:
źródło
for...in
pętli do iteracji po tablicach.Możesz to łatwo zrobić za pomocą mapy i sortowania:
Możesz tasować tablice polimorficzne, a sortowanie jest tak losowe jak Math.random, co jest wystarczające do większości celów.
Ponieważ elementy są sortowane według spójnych kluczy, które nie są regenerowane przy każdej iteracji, a każde porównanie pobiera z tego samego rozkładu, wszelkie nielosowości w rozkładzie Math.random są anulowane.
Prędkość
Złożoność czasowa wynosi O (N log N), podobnie jak szybkie sortowanie. Złożoność przestrzeni wynosi O (N). Nie jest to tak skuteczne jak tasowanie Fischera Yatesa, ale moim zdaniem kod jest znacznie krótszy i bardziej funkcjonalny. Jeśli masz dużą tablicę, z pewnością powinieneś użyć Fischera Yatesa. Jeśli masz małą tablicę zawierającą kilkaset przedmiotów, możesz to zrobić.
źródło
Użyj biblioteki underscore.js. Metoda
_.shuffle()
jest dobra w tym przypadku. Oto przykład z metodą:źródło
shuffle
funkcję.NOWY!
Krótszy i prawdopodobnie * szybszy algorytm losowania Fisher-Yatesa
rozmiar skryptu (z fy jako nazwą funkcji): 90 bajtów
PRÓBNY http://jsfiddle.net/vvpoma8w/
* szybciej prawdopodobnie we wszystkich przeglądarkach oprócz Chrome.
Jeśli masz jakieś pytania, po prostu zapytaj.
EDYTOWAĆ
tak to jest szybsze
WYDAJNOŚĆ: http://jsperf.com/fyshuffle
za pomocą najczęściej głosowanych funkcji.
EDYCJA Nastąpiło przekroczenie obliczeń (nie trzeba --c + 1) i nikt tego nie zauważył
krótszy (4 bajty) i szybszy (przetestuj!).
Buforowanie gdzie indziej,
var rnd=Math.random
a następnie użyciernd()
również nieznacznie zwiększy wydajność na dużych tablicach.http://jsfiddle.net/vvpoma8w/2/
Wersja do odczytu (użyj oryginalnej wersji. Jest wolniejsza, zmienne są bezużyteczne, takie jak zamknięcia & ";", sam kod jest również krótszy ... może przeczytaj to Jak „zminimalizować” kod JavaScript , ale nie jesteś w stanie skompresuj następujący kod w minizatorach javascript, takich jak powyższy.)
źródło
fy
ishuffle prototype
,fy
konsekwentnie dostaję się na dole w przeglądarce Chrome 37 w systemie OS X 10.9.5 (81% wolniej ~ 20 tys. Operacji w porównaniu do ~ 100 tys.), A Safari 7.1 - do ~ 8% wolniej. YMMV, ale nie zawsze jest szybszy. jsperf.com/fyshuffle/3Edycja: ta odpowiedź jest niepoprawna
Zobacz komentarze i https://stackoverflow.com/a/18650169/28234 . Zostaje tutaj w celach informacyjnych, ponieważ pomysł nie jest rzadki.
Bardzo prosty sposób na małe tablice to po prostu:
Prawdopodobnie nie jest zbyt wydajny, ale w przypadku małych tablic działa to dobrze. Oto przykład, dzięki któremu możesz zobaczyć, jak losowy (lub nie) i czy pasuje do Twojej skrzynki użytkownika, czy nie.
źródło
Niezawodny, wydajny, krótki
Niektóre rozwiązania na tej stronie nie są niezawodne (tylko częściowo losują tablicę). Inne rozwiązania są znacznie mniej wydajne. Za pomocą
testShuffleArrayFun
(patrz poniżej) możemy przetestować funkcje tasowania tablicy pod kątem niezawodności i wydajności. Następujące rozwiązania: niezawodne, wydajne i krótkie (przy użyciu składni ES6)[Testy porównawcze zostały wykonane przy użyciu
testShuffleArrayFun
innych rozwiązań w Google Chrome]Shuffle Array In place
ES6 Czysty, iteracyjny
Test niezawodności i wydajności
Jak widać na tej stronie, w przeszłości oferowane były tutaj nieprawidłowe rozwiązania. Napisałem i użyłem poniższej funkcji do przetestowania dowolnych funkcji randomizujących tablicę czystą (bez skutków ubocznych).
Inne rozwiązania
Inne rozwiązania dla zabawy.
ES6 Pure, rekurencyjny
ES6 Pure przy użyciu array.map
ES6 Pure przy użyciu array.reduce
źródło
[array[i], array[rand]]=[array[rand], array[i]]
? Może możesz nakreślić, jak to działa. Dlaczego decydujesz się na iterację w dół?Dodanie do odpowiedzi @Laurens Holsts. Jest to skompresowane w 50%.
źródło
var b =
w pętli zamiast deklarować b poza pętlą i przypisywać jąb =
w pętli?Edycja: ta odpowiedź jest niepoprawna
Zobacz https://stackoverflow.com/a/18650169/28234 . Zostaje tutaj w celach informacyjnych, ponieważ pomysł nie jest rzadki.
https://javascript.info/task/shuffle
źródło
W ES2015 możesz użyć tego:
Stosowanie:
źródło
n >>> 0
zamiast~~n
. Indeksy tablic mogą być wyższe niż 2³¹-1.Znalazłem ten wariant w odpowiedzi na „usunięte przez autora” na duplikacie tego pytania. W przeciwieństwie do niektórych innych odpowiedzi, które mają już wiele pozytywnych opinii, jest to:
shuffled
nazwa zamiastshuffle
)Oto jsfiddle pokazujący, że jest w użyciu .
źródło
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- nie daje losowego sortowania, a jeśli go użyjesz, możesz się wstydzić: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html.sort(function(a,b){ return a[0] - b[0]; })
jeśli chcesz, aby sortowanie porównywało wartości liczbowo. Domyślny.sort()
komparator to leksykograficzny, co oznacza, że będzie uważany10
za mniejszy od tego2
czasu1
mniej niż2
.Math.random()
produkuje. (to znaczy porządek leksykograficzny jest taki sam jak porządek numeryczny w przypadku liczb od 0 (włącznie) do 1 (wyłącznie))źródło
źródło
Możesz to łatwo zrobić za pomocą:
Odwołaj się do JavaScript Sorting Arrays
źródło
Rozwiązanie rekurencyjne:
źródło
Fisher-Yates tasuje w javascript. Zamieszczam to tutaj, ponieważ użycie dwóch funkcji narzędziowych (swap i randInt) wyjaśnia algorytm w porównaniu z innymi odpowiedziami tutaj.
źródło
Przede wszystkim spójrz tutaj aby uzyskać świetne wizualne porównanie różnych metod sortowania w javascript.
Po drugie, jeśli rzucisz okiem na powyższy link, zobaczysz, że
random order
sortowanie wydaje się działać stosunkowo dobrze w porównaniu z innymi metodami, a jednocześnie jest niezwykle łatwe i szybkie do wdrożenia, jak pokazano poniżej:Edycja : jak wskazali @gregers, funkcja porównywania jest wywoływana z wartościami, a nie indeksami, dlatego musisz jej użyć
indexOf
. Zauważ, że ta zmiana sprawia, że kod jest mniej odpowiedni dla większych tablic, ponieważindexOf
działa w czasie O (n).źródło
Array.prototype.sort
przekazuje dwie wartości jakoa
ib
, a nie indeks. Więc ten kod nie działa.funkcja losowania, która nie zmienia tablicy źródłowej
Aktualizacja : tutaj sugeruję stosunkowo prosty (nie ze złożoności punktu widzenia ) i krótki algorytm, który dobrze sobie poradzi z małymi tablicami, ale na pewno będzie kosztował znacznie więcej niż klasyczny algorytm Durstenfelda , gdy masz do czynienia z dużymi tablicami. Możesz znaleźć Durstenfeld w jednej z najlepszych odpowiedzi na to pytanie.
Oryginalna odpowiedź:
Jeśli nie chcesz, aby funkcja odtwarzania losowego mutowała tablicę źródłową , możesz skopiować ją do zmiennej lokalnej, a następnie zrobić resztę za pomocą prostej logiki losowania .
Tasowanie logiki : wybierz losowy indeks, a następnie dodaj odpowiedni element do tablicy wyników i usuń go z kopii tablicy źródłowej . Powtarzaj tę akcję, aż tablica źródłowa się opróżni .
A jeśli naprawdę chcesz tego krótko, oto, jak daleko mogę się dostać:
źródło
splice
ponieważ jest to strasznie nieefektywny sposób robienia tego, co nazywają „wykreślaniem”. Jeśli nie chcesz mutować oryginalnej tablicy, po prostu skopiuj ją, a następnie przetasuj tę kopię w miejscu, używając znacznie bardziej wydajnego wariantu Durstenfeld.splice
metody, aby utworzyć kopię tak:source = array.slice();
.Oto najprostszy jeden,
na przykład możesz to sprawdzić tutaj
źródło
jeszcze jedna implementacja Fisher-Yatesa, wykorzystująca tryb ścisły:
źródło
Wszystkie pozostałe odpowiedzi oparte są na Math.random (), która jest szybka, ale nie nadaje się do randomizacji na poziomie kryptograficznym.
Poniższy kod za pomocą dobrze znanego
Fisher-Yates
algorytmu, przy wykorzystaniuWeb Cryptography API
na poziomie kryptograficznego randomizacji .źródło
Nowoczesne krótkie rozwiązanie wbudowane wykorzystujące funkcje ES6:
(do celów edukacyjnych)
źródło
Modyfikacja prosty z CoolAJ86 za odpowiedź , która nie zmienia oryginalnej tablicy:
źródło
Chociaż jest już wiele zalecanych implementacji, ale uważam, że możemy uczynić je krótszym i łatwiejszym przy użyciu pętli forEach, więc nie musimy się martwić o obliczanie długości tablicy, a także możemy bezpiecznie uniknąć używania tymczasowej zmiennej.
źródło
Żeby mieć palec w torcie. Tutaj przedstawiam rekurencyjną implementację shuffle Fishera Yatesa (tak myślę). Daje jednolitą losowość.
Uwaga:
~~
(podwójny operator tyldy) w rzeczywistości zachowuje się jakMath.floor()
dla dodatnich liczb rzeczywistych. To tylko skrót.Edycja: Powyższy kod to O (n ^ 2) ze względu na zastosowanie,
.splice()
ale możemy wyeliminować splatanie i tasowanie w O (n) za pomocą sztuczki wymiany.Problem polega na tym, że JS nie może współpracować z dużymi rekurencjami. W tym konkretnym przypadku rozmiar tablicy jest ograniczony do około 3000 ~ 7000, w zależności od silnika przeglądarki i niektórych nieznanych faktów.
źródło
Losuj tablicę
źródło
-1
for za, jak<
nie<=
najkrótsza
arrayShuffle
funkcjaźródło
Z teoretycznego punktu widzenia najbardziej eleganckim sposobem na zrobienie tego, moim skromnym zdaniem, jest uzyskanie pojedynczej liczby losowej od 0 do n! -1 i obliczenie odwzorowania jeden na jeden
{0, 1, …, n!-1}
dla wszystkich permutacji(0, 1, 2, …, n-1)
. Tak długo, jak możesz użyć (pseudo-) losowego generatora wystarczająco niezawodnego, aby uzyskać taką liczbę bez znaczącego odchylenia, masz wystarczająco dużo informacji, aby osiągnąć to, czego chcesz, bez potrzeby kilku innych liczb losowych.Podczas obliczania liczb zmiennoprzecinkowych podwójnej precyzji IEEE754 można oczekiwać, że generator losowy zapewni około 15 miejsc po przecinku. Ponieważ masz 15! = 1 307 674,368,000 (z 13 cyframi), możesz używać następujących funkcji z tablicami zawierającymi do 15 elementów i zakładać, że nie będzie znaczącego odchylenia z tablicami zawierającymi do 14 elementów. Jeśli pracujesz nad problemem o stałym rozmiarze, wymagającym wielokrotnego obliczenia tej operacji losowania, możesz wypróbować następujący kod, który może być szybszy niż inne kody, ponieważ używa
Math.random
tylko jednego kodu (wymaga to jednak kilku operacji kopiowania).Następująca funkcja nie będzie używana, ale i tak ją daję; zwraca indeks danej permutacji
(0, 1, 2, …, n-1)
zgodnie z odwzorowaniem jeden na jeden zastosowanym w tym komunikacie (najbardziej naturalny przy wyliczaniu permuacji); przeznaczony jest do pracy z maksymalnie 16 elementami:Odwrotność poprzedniej funkcji (wymagana dla twojego pytania) jest poniżej; przeznaczony jest do pracy z maksymalnie 16 elementami; zwraca permutacji porządku n o
(0, 1, 2, …, s-1)
:Teraz chcesz jedynie:
Powinien działać dla maksymalnie 16 elementów z niewielkim teoretycznym nastawieniem (choć z praktycznego punktu widzenia nie jest to zauważalne); może być postrzegany jako w pełni użyteczny dla 15 elementów; z tablicami zawierającymi mniej niż 14 elementów możesz bezpiecznie założyć, że nie będzie absolutnie żadnego błędu.
źródło