Pracuję nad „jak uzyskać dostęp do elementów losowo z tablicy w javascript”. Znalazłem wiele linków na ten temat. Like: Pobierz losowy element z tablicy JavaScript
var item = items[Math.floor(Math.random()*items.length)];
Ale w tym przypadku możemy wybrać tylko jedną pozycję z tablicy. Jeśli chcemy więcej niż jednego elementu, jak możemy to osiągnąć? Jak możemy uzyskać więcej niż jeden element z tablicy?
javascript
jquery
html
arrays
Shyam Dixit
źródło
źródło
Odpowiedzi:
Wypróbuj tę nieniszczącą (i szybką ) funkcję:
function getRandom(arr, n) { var result = new Array(n), len = arr.length, taken = new Array(len); if (n > len) throw new RangeError("getRandom: more elements taken than available"); while (n--) { var x = Math.floor(Math.random() * len); result[n] = arr[x in taken ? taken[x] : x]; taken[x] = --len in taken ? taken[len] : len; } return result; }
źródło
Set
(który nie był dostępny w '13: - /)Tylko dwie linie:
// Shuffle array const shuffled = array.sort(() => 0.5 - Math.random()); // Get sub-array of first n elements after shuffled let selected = shuffled.slice(0, n);
DEMO :
źródło
let random = array.sort(() => .5 - Math.random()).slice(0,n)
Jest tutaj jedyne w swoim rodzaju rozwiązanie
array.sort(() => Math.random() - Math.random()).slice(0, n)
źródło
Portowanie
.sample
ze standardowej biblioteki Pythona:function sample(population, k){ /* Chooses k unique random elements from a population sequence or set. Returns a new list containing elements from the population while leaving the original population unchanged. The resulting list is in selection order so that all sub-slices will also be valid random samples. This allows raffle winners (the sample) to be partitioned into grand prize and second place winners (the subslices). Members of the population need not be hashable or unique. If the population contains repeats, then each occurrence is a possible selection in the sample. To choose a sample in a range of integers, use range as an argument. This is especially fast and space efficient for sampling from a large population: sample(range(10000000), 60) Sampling without replacement entails tracking either potential selections (the pool) in a list or previous selections in a set. When the number of selections is small compared to the population, then tracking selections is efficient, requiring only a small set and an occasional reselection. For a larger number of selections, the pool tracking method is preferred since the list takes less space than the set and it doesn't suffer from frequent reselections. */ if(!Array.isArray(population)) throw new TypeError("Population must be an array."); var n = population.length; if(k < 0 || k > n) throw new RangeError("Sample larger than population or is negative"); var result = new Array(k); var setsize = 21; // size of a small set minus size of an empty list if(k > 5) setsize += Math.pow(4, Math.ceil(Math.log(k * 3) / Math.log(4))) if(n <= setsize){ // An n-length list is smaller than a k-length set var pool = population.slice(); for(var i = 0; i < k; i++){ // invariant: non-selected at [0,n-i) var j = Math.random() * (n - i) | 0; result[i] = pool[j]; pool[j] = pool[n - i - 1]; // move non-selected item into vacancy } }else{ var selected = new Set(); for(var i = 0; i < k; i++){ var j = Math.random() * n | 0; while(selected.has(j)){ j = Math.random() * n | 0; } selected.add(j); result[i] = population[j]; } } return result; }
Implementacja przeniesiona z Lib / random.py .
Uwagi:
setsize
jest ustawiony na podstawie charakterystyk w Pythonie pod kątem wydajności. Chociaż nie został dostosowany do JavaScript, algorytm będzie nadal działał zgodnie z oczekiwaniami.Array.prototype.sort
. Algorytm ten jednak gwarantuje zakończenie w określonym czasie.Set
zaimplementowanych, zestaw można zastąpić rozszerzeniemArray
i.has(j)
zastąpić.indexOf(j) > -1
.Wydajność w stosunku do zaakceptowanej odpowiedzi:
źródło
Zdobycie 5 losowych przedmiotów bez zmiany oryginalnej tablicy:
const n = 5; const sample = items .map(x => ({ x, r: Math.random() })) .sort((a, b) => a.r - b.r) .map(a => a.x) .slice(0, n);
(Nie używaj tego do dużych list)
źródło
stworzyć funkcję, która to robi:
var getMeRandomElements = function(sourceArray, neededElements) { var result = []; for (var i = 0; i < neededElements; i++) { result.push(sourceArray[Math.floor(Math.random()*sourceArray.length)]); } return result; }
powinieneś również sprawdzić, czy sourceArray ma wystarczającą liczbę elementów do zwrócenia. a jeśli chcesz, aby zwracane były unikalne elementy, powinieneś usunąć wybrany element z sourceArray.
źródło
sourceArray
wiele razy.Składnia ES6
const pickRandom = (arr,count) => { let _arr = [...arr]; return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] ); }
źródło
Jeśli chcesz losowo pobierać elementy z tablicy w pętli bez powtórzeń, możesz usunąć wybrany element z tablicy za pomocą
splice
:var items = [1, 2, 3, 4, 5]; var newItems = []; for (var i = 0; i < 3; i++) { var idx = Math.floor(Math.random() * items.length); newItems.push(items[idx]); items.splice(idx, 1); } console.log(newItems);
źródło
1
jestdeleteCount
wskazanie liczby starych elementów tablicy usunąć. (Nawiasem mówiąc, zredukowałem ostatnie dwie linie donewItems.push(items.splice(idx, 1)[0])
).lodash ( https://lodash.com/ )
_.sample
oraz_.sampleSize
.Pobiera jeden lub n losowych elementów w unikatowych kluczach z kolekcji do rozmiaru kolekcji.
_.sample([1, 2, 3, 4]); // => 2 _.sampleSize([1, 2, 3], 2); // => [3, 1] _.sampleSize([1, 2, 3], 3); // => [2, 3, 1]
źródło
_
? To nie jest standardowy obiekt JavaScript.Array.prototype.getnkill = function() { var a = Math.floor(Math.random()*this.length); var dead = this[a]; this.splice(a,1); return dead; } //.getnkill() removes element in the array //so if you like you can keep a copy of the array first: //var original= items.slice(0); var item = items.getnkill(); var anotheritem = items.getnkill();
źródło
Oto ładnie napisana wersja. Nie zawodzi. Zwraca przetasowaną tablicę, jeśli rozmiar próbki jest większy niż długość oryginalnej tablicy.
function sampleArr<T>(arr: T[], size: number): T[] { const setOfIndexes = new Set<number>(); while (setOfIndexes.size < size && setOfIndexes.size < arr.length) { setOfIndexes.add(randomIntFromInterval(0, arr.length - 1)); } return Array.from(setOfIndexes.values()).map(i => arr[i]); } const randomIntFromInterval = (min: number, max: number): number => Math.floor(Math.random() * (max - min + 1) + min);
źródło
W tej odpowiedzi chcę podzielić się z wami testem, że muszę znać najlepszą metodę dającą równe szanse wszystkim elementom na losową podtablicę.
Metoda 01
array.sort(() => Math.random() - Math.random()).slice(0, n)
przy użyciu tej metody niektóre elementy mają większe szanse w porównaniu z innymi.
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) { let occ = 0 for (let index = 0; index < iterations; index++) { const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize] /** Wrong Method */ const arr = myArray.sort(function() { return val= .5 - Math.random(); }); if(arr[0]===number) { occ ++ } } console.log("Probability of ",number, " = ",occ*100 /iterations,"%") } calculateProbability(0) calculateProbability(0) calculateProbability(0) calculateProbability(50) calculateProbability(50) calculateProbability(50) calculateProbability(25) calculateProbability(25) calculateProbability(25)
Metoda 2
Stosując tę metodę, elementy mają takie samo prawdopodobieństwo:
const arr = myArray .map((a) => ({sort: Math.random(), value: a})) .sort((a, b) => a.sort - b.sort) .map((a) => a.value)
calculateProbability = function(number=0 ,iterations=10000,arraySize=100) { let occ = 0 for (let index = 0; index < iterations; index++) { const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize] /** Correct Method */ const arr = myArray .map((a) => ({sort: Math.random(), value: a})) .sort((a, b) => a.sort - b.sort) .map((a) => a.value) if(arr[0]===number) { occ ++ } } console.log("Probability of ",number, " = ",occ*100 /iterations,"%") } calculateProbability(0) calculateProbability(0) calculateProbability(0) calculateProbability(50) calculateProbability(50) calculateProbability(50) calculateProbability(25) calculateProbability(25) calculateProbability(25)
Prawidłowa odpowiedź znajduje się w poniższym linku: https://stackoverflow.com/a/46545530/3811640
źródło
Nieniszczący
funkcjonalny styl programowania 2020 , działający w niezmiennym kontekście.
const _randomslice = (ar, size) => { let new_ar = [...ar]; new_ar.splice(Math.floor(Math.random()*ar.length),1); return ar.length <= (size+1) ? new_ar : _randomslice(new_ar, size); } console.log(_randomslice([1,2,3,4,5],2));
źródło
_shuffle
funkcja?size >= ar.length
wynik będziesize-1
EDYCJA : To rozwiązanie jest wolniejsze niż inne przedstawione tutaj (które łączą tablicę źródłową), jeśli chcesz uzyskać tylko kilka elementów. Szybkość tego rozwiązania zależy tylko od liczby elementów w pierwotnej tablicy, podczas gdy szybkość rozwiązania splicingu zależy od liczby elementów wymaganych w macierzy wyjściowej.
Jeśli chcesz nie powtarzających się losowych elementów, możesz przetasować swoją tablicę, a następnie uzyskać tylko tyle, ile chcesz:
function shuffle(array) { var counter = array.length, temp, index; // While there are elements in the array while (counter--) { // Pick a random index index = (Math.random() * counter) | 0; // And swap the last element with it temp = array[counter]; array[counter] = array[index]; array[index] = temp; } return array; } var arr = [0,1,2,3,4,5,7,8,9]; var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed randoms.length = 4; // get 4 random elements
DEMO: http://jsbin.com/UHUHuqi/1/edit
Funkcja Shuffle pobrana stąd: https://stackoverflow.com/a/6274398/1669279
źródło
O(n+k)
(n elementów w tablicy, chcesz ich k), podczas gdyO(k)
byłby możliwy (i optymalny).O(2n)
które można by zredukowaćO(n+k)
, gdybyś zmienił pętlę nawhile (counter-- > len-k)
i usunął z niej ostatnie (zamiast pierwszych)k
elementy. Rzeczywiściesplice(i, 1)
nie maO(1)
, aleO(k)
rozwiązanie jest nadal możliwe (zobacz moją odpowiedź). Jednak złożoność przestrzeni kosmicznej pozostajeO(n+k)
niestety, ale może się stać wO(2k)
zależności od implementacji rzadkiej tablicy.Potrzebowałem funkcji do rozwiązania tego rodzaju problemu, więc udostępniam ją tutaj.
const getRandomItem = function(arr) { return arr[Math.floor(Math.random() * arr.length)]; } // original array let arr = [4, 3, 1, 6, 9, 8, 5]; // number of random elements to get from arr let n = 4; let count = 0; // new array to push random item in let randomItems = [] do { let item = getRandomItem(arr); randomItems.push(item); // update the original array and remove the recently pushed item arr.splice(arr.indexOf(item), 1); count++; } while(count < n); console.log(randomItems); console.log(arr);
Uwaga: jeśli
n = arr.length
to w zasadzie tasujesz tablicęarr
irandomItems
zwracasz tę shuffled tablicę.Próbny
źródło
Oto zoptymalizowana wersja kodu przeniesionego z Pythona przez @Derek, z dodaną opcją destrukcyjną (w miejscu), która sprawia, że jest to najszybszy możliwy algorytm, jeśli możesz z nim skorzystać. W przeciwnym razie albo tworzy pełną kopię, albo w przypadku niewielkiej liczby elementów żądanych z dużej tablicy przełącza się na algorytm oparty na wyborze.
// Chooses k unique random elements from pool. function sample(pool, k, destructive) { var n = pool.length; if (k < 0 || k > n) throw new RangeError("Sample larger than population or is negative"); if (destructive || n <= (k <= 5 ? 21 : 21 + Math.pow(4, Math.ceil(Math.log(k*3) / Math.log(4))))) { if (!destructive) pool = Array.prototype.slice.call(pool); for (var i = 0; i < k; i++) { // invariant: non-selected at [i,n) var j = i + Math.random() * (n - i) | 0; var x = pool[i]; pool[i] = pool[j]; pool[j] = x; } pool.length = k; // truncate return pool; } else { var selected = new Set(); while (selected.add(Math.random() * n | 0).size < k) {} return Array.prototype.map.call(selected, i => pool[i]); } }
W porównaniu z implementacją Dereka, pierwszy algorytm jest znacznie szybszy w Firefoksie, a nieco wolniejszy w Chrome, chociaż teraz ma destrukcyjną opcję - najbardziej wydajną. Drugi algorytm jest po prostu szybszy o 5-15%. Staram się nie podawać żadnych konkretnych liczb, ponieważ różnią się one w zależności od k i ni prawdopodobnie nie będą miały żadnego znaczenia w przyszłości w nowych wersjach przeglądarek.
Heurystyka, która dokonuje wyboru między algorytmami, pochodzi z kodu Pythona. Zostawiłem go tak, jak jest, chociaż czasami wybiera wolniejszy. Powinien być zoptymalizowany pod kątem JS, ale jest to złożone zadanie, ponieważ wydajność przypadków narożnych zależy od przeglądarki i ich wersji. Na przykład, gdy spróbujesz wybrać 20 z 1000 lub 1050, przełączy się odpowiednio na pierwszy lub drugi algorytm. W tym przypadku pierwszy z nich działa 2x szybciej niż drugi w Chrome 80, ale 3x wolniej w Firefoksie 74.
źródło
log(k*3, 4)
ponieważ JS nie mabase
argumentu. Należylog(k*3)/log(4)
pool
jako plikuresult
. Ponieważ obcięto,pool
nie można go już używać jako źródła do samplowania i następnym razem będzieszsample
musiał ponownie utworzyćpool
z jakiegoś źródła. Implementacja Dereka tylko tasuje pulę, więc można ją idealnie ponownie wykorzystać do pobierania próbek bez odtwarzania. Uważam, że jest to najczęstszy przypadek użycia.log
w kodzie moim i Dereka. Jeśli chodzi o ponowne użyciepool
, po prostu nie włączaj tejdestructive
opcji, apool
argument zostanie przesłany w cień z kopią.Wyodrębnia losowe elementy z srcArray jeden po drugim, póki wystarcza lub nie ma już elementów w srcArray do wyodrębnienia. Szybki i niezawodny.
function getNRandomValuesFromArray(srcArr, n) { // making copy to do not affect original srcArray srcArr = srcArr.slice(); resultArr = []; // while srcArray isn't empty AND we didn't enough random elements while (srcArr.length && resultArr.length < n) { // remove one element from random position and add this element to the result array resultArr = resultArr.concat( // merge arrays srcArr.splice( // extract one random element Math.floor(Math.random() * srcArr.length), 1 ) ); } return resultArr; }
źródło
2019
To jest to samo, co odpowiedź Laurynasa Mališauskasa , tyle że elementy są unikalne (bez duplikatów).
var getMeRandomElements = function(sourceArray, neededElements) { var result = []; for (var i = 0; i < neededElements; i++) { var index = Math.floor(Math.random() * sourceArray.length); result.push(sourceArray[index]); sourceArray.splice(index, 1); } return result; }
A teraz, aby odpowiedzieć na oryginalne pytanie „Jak uzyskać wiele losowych elementów przez jQuery”, proszę bardzo:
var getMeRandomElements = function(sourceArray, neededElements) { var result = []; for (var i = 0; i < neededElements; i++) { var index = Math.floor(Math.random() * sourceArray.length); result.push(sourceArray[index]); sourceArray.splice(index, 1); } return result; } var $set = $('.someClass');// <<<<< change this please var allIndexes = []; for(var i = 0; i < $set.length; ++i) { allIndexes.push(i); } var totalRandom = 4;// <<<<< change this please var randomIndexes = getMeRandomElements(allIndexes, totalRandom); var $randomElements = null; for(var i = 0; i < randomIndexes.length; ++i) { var randomIndex = randomIndexes[i]; if($randomElements === null) { $randomElements = $set.eq(randomIndex); } else { $randomElements.add($set.eq(randomIndex)); } } // $randomElements is ready $randomElements.css('backgroundColor', 'red');
źródło
Oto funkcja, której używam, która umożliwia łatwe próbkowanie tablicy z wymianą lub bez:
// Returns a random sample (either with or without replacement) from an array const randomSample = (arr, k, withReplacement = false) => { let sample; if (withReplacement === true) { // sample with replacement sample = Array.from({length: k}, () => arr[Math.floor(Math.random() * arr.length)]); } else { // sample without replacement if (k > arr.length) { throw new RangeError('Sample size must be less than or equal to array length when sampling without replacement.') } sample = arr.map(a => [a, Math.random()]).sort((a, b) => { return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]); }; return sample; };
Korzystanie z niego jest proste:
Bez zamiany (zachowanie domyślne)
randomSample([1, 2, 3], 2)
może wrócić[2, 1]
Z wymianą
randomSample([1, 2, 3, 4, 5, 6], 4)
może wrócić[2, 3, 3, 2]
źródło
var getRandomElements = function(sourceArray, requiredLength) { var result = []; while(result.length<requiredLength){ random = Math.floor(Math.random()*sourceArray.length); if(result.indexOf(sourceArray[random])==-1){ result.push(sourceArray[random]); } } return result; }
źródło
Nie mogę uwierzyć, że nikt nie wspomniał o tej metodzie, całkiem przejrzystej i prostej.
const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);
źródło
Oto najbardziej poprawna odpowiedź, która da ci elementy losowe + unikalne.
function randomize(array, n) { var final = []; array = array.filter(function(elem, index, self) { return index == self.indexOf(elem); }).sort(function() { return 0.5 - Math.random() }); var len = array.length, n = n > len ? len : n; for(var i = 0; i < n; i ++) { final[i] = array[i]; } return final; } // randomize([1,2,3,4,5,3,2], 4); // Result: [1, 2, 3, 5] // Something like this
źródło
items.sort (() => (Math.random ()> 0.5? 1: -1)). slice (0, count);
źródło