Jak wybrać losowy element z zestawu? Szczególnie interesuje mnie wybranie losowego elementu z HashSet lub LinkedHashSet w Javie. Mile widziane są również rozwiązania dla innych języków.
Powinieneś określić kilka warunków, aby zobaczyć, czy naprawdę tego chcesz. - W jakich czasach będziesz wybierać losowy element? - Czy dane muszą być przechowywane w HashSet lub LinkedHashSet, ani nie są dostępne losowo. - Czy hasz jest duży? Czy klucze są małe?
David Nehme,
Odpowiedzi:
88
int size = myHashSet.size();int item =newRandom().nextInt(size);// In real life, the Random object should be rather more shared than thisint i =0;for(Object obj : myhashSet){if(i == item)return obj;
i++;}
Jeśli myHashSet jest duży, będzie to raczej powolne rozwiązanie, ponieważ średnio potrzeba (n / 2) iteracji, aby znaleźć losowy obiekt.
daniel
6
jeśli twoje dane są w zestawie skrótów, potrzebujesz O (n) czasu. Nie da się tego obejść, jeśli wybierasz tylko jeden element, a dane są przechowywane w HashSet.
David Nehme,
8
@David Nehme: Jest to wada specyfikacji HashSet w Javie. W C ++ typowe jest, aby mieć bezpośredni dostęp do zasobników, które tworzą hashset, co pozwala nam efektywniej wybierać losowe elementy. Jeśli w Javie potrzebne są elementy losowe, warto zdefiniować niestandardowy zestaw skrótów, który pozwoli użytkownikowi zajrzeć pod maskę. Zobacz [boost's docs] [1], aby uzyskać więcej informacji na ten temat. [1] boost.org/doc/libs/1_43_0/doc/html/unordered/buckets.html
Aaron McDaid,
11
Jeśli zestaw nie jest mutowany przy wielu dostępach, możesz skopiować go do tablicy, a następnie uzyskać dostęp do O (1). Po prostu użyj myHashSet.toArray ()
ykaganovich
2
@ykaganovich czy to nie pogorszy sprawy, skoro zestaw musiałby zostać skopiowany do nowej tablicy? docs.oracle.com/javase/7/docs/api/java/util/… „ta metoda musi przydzielić nową tablicę, nawet jeśli ta kolekcja jest
Niesamowite! Nie ma tam żadnych odwołań krzyżowych w dokumencie java! Podobnie jak random.shuffle ()
smci,
25
Ale działa to tylko z Listami, tj. Strukturami, które mają funkcję .get ().
bourbaki4481472
4
@ bourbaki4481472 jest absolutnie poprawne. Działa to tylko dla tych kolekcji rozszerzających Listinterfejs, a nie dla Setinterfejsu omawianego przez OP.
Thomas
31
Szybkie rozwiązanie dla języka Java za pomocą znaków ArrayListi HashMap: [element -> indeks].
Motywacja: potrzebowałem zestawu przedmiotów z RandomAccesswłaściwościami, zwłaszcza aby wybrać losowy przedmiot z zestawu (patrz pollRandommetoda). Losowa nawigacja w drzewie binarnym nie jest dokładna: drzewa nie są idealnie zrównoważone, co nie prowadziłoby do równomiernego rozmieszczenia.
publicclassRandomSet<E>extendsAbstractSet<E>{List<E> dta =newArrayList<E>();Map<E,Integer> idx =newHashMap<E,Integer>();publicRandomSet(){}publicRandomSet(Collection<E> items){for(E item : items){
idx.put(item, dta.size());
dta.add(item);}}@Overridepublicboolean add(E item){if(idx.containsKey(item)){returnfalse;}
idx.put(item, dta.size());
dta.add(item);returntrue;}/**
* Override element at position <code>id</code> with last element.
* @param id
*/public E removeAt(int id){if(id >= dta.size()){returnnull;}
E res = dta.get(id);
idx.remove(res);
E last = dta.remove(dta.size()-1);// skip filling the hole if last is removedif(id < dta.size()){
idx.put(last, id);
dta.set(id, last);}return res;}@Overridepublicboolean remove(Object item){@SuppressWarnings(value ="element-type-mismatch")Integer id = idx.get(item);if(id ==null){returnfalse;}
removeAt(id);returntrue;}public E get(int i){return dta.get(i);}public E pollRandom(Random rnd){if(dta.isEmpty()){returnnull;}int id = rnd.nextInt(dta.size());return removeAt(id);}@Overridepublicint size(){return dta.size();}@OverridepublicIterator<E> iterator(){return dta.iterator();}}
Cóż, to by działało, ale pytanie dotyczyło interfejsu Set. To rozwiązanie zmusza użytkowników do posiadania konkretnych referencji typu RandomSet.
Johan Tidén
Naprawdę podoba mi się to rozwiązanie, ale nie jest bezpieczne wątkowo, mogą wystąpić niedokładności między mapą a listą, więc dodałbym kilka zsynchronizowanych bloków
Kostas Chalkias
@KonstantinosChalkias wbudowane kolekcje również nie są bezpieczne wątkowo. Tylko te z nazwą Concurrentsą naprawdę bezpieczne, te owinięte Collections.synchronized()są pół-bezpieczne. Również OP nie powiedział nic o współbieżności, więc jest to poprawna i dobra odpowiedź.
TWiStErRob
Iterator zwrócony tutaj nie powinien być w stanie usunąć elementów z dta(można to osiągnąć Iterators.unmodifiableIteratorna przykład za pomocą guawy ). W przeciwnym razie domyślne implementacje, np. RemoveAll i retainAll w AbstractSet i jego elementy nadrzędne pracujące z tym iteratorem, zepsują RandomSet!
wyemitowano
Niezłe rozwiązanie. W rzeczywistości możesz użyć drzewa, jeśli każdy węzeł zawiera liczbę węzłów w poddrzewie, z którego pochodzi. Następnie oblicz losową liczbę rzeczywistą w zakresie 0..1 i podejmij ważoną 3-kierunkową decyzję (wybierz bieżący węzeł lub zejdź do lewego lub prawego poddrzewa) w każdym węźle na podstawie liczby węzłów. Ale imo Twoje rozwiązanie jest o wiele przyjemniejsze.
Gene
30
Jest to szybsze niż pętla for-each w akceptowanej odpowiedzi:
int index = rand.nextInt(set.size());Iterator<Object> iter = set.iterator();for(int i =0; i < index; i++){
iter.next();}return iter.next();
Konstrukcja for-each wywołuje Iterator.hasNext()każdą pętlę, ale ponieważ index < set.size()to sprawdzenie jest niepotrzebnym narzutem. Widziałem wzrost prędkości o 10-20%, ale YMMV. (Ponadto kompiluje się bez konieczności dodawania dodatkowej instrukcji return).
Zwróć uwagę, że ten kod (i większość innych odpowiedzi) można zastosować do dowolnej kolekcji, a nie tylko do zestawu. W ogólnej formie metody:
publicstatic<E> E choice(Collection<?extends E> coll,Random rand){if(coll.size()==0){returnnull;// or throw IAE, if you prefer}int index = rand.nextInt(coll.size());if(coll instanceofList){// optimizationreturn((List<?extends E>) coll).get(index);}else{Iterator<?extends E> iter = coll.iterator();for(int i =0; i < index; i++){
iter.next();}return iter.next();}}
Jeśli chcesz to zrobić w Javie, powinieneś rozważyć skopiowanie elementów do jakiejś kolekcji o swobodnym dostępie (takiej jak ArrayList). Ponieważ, chyba że twój zestaw jest mały, dostęp do wybranego elementu będzie kosztowny (O (n) zamiast O (1)). [ed: kopia listy jest również O (n)]
Alternatywnie możesz poszukać innej implementacji zestawu, która bardziej odpowiada Twoim wymaganiom. Zestaw ListOrderedSet z Commons Collections wygląda obiecująco.
Kopiowanie do listy będzie kosztować O (n) czasu, a także zużyje O (n) pamięci, więc dlaczego miałoby to być lepszym wyborem niż bezpośrednie pobieranie z mapy?
mdma
12
Zależy to od tego, ile razy chcesz wybierać z zestawu. Kopiowanie jest jednorazową operacją, a następnie możesz wybierać z zestawu tyle razy, ile potrzebujesz. Jeśli wybierasz tylko jeden element, to tak, kopia nie przyspiesza rzeczy.
Dan Dyer,
To tylko jednorazowa operacja, jeśli chcesz mieć możliwość wybierania z powtórzeniami. Jeśli chcesz, aby wybrana pozycja została usunięta z zestawu, wrócisz do O (n).
TurnipEntropy
12
W Javie 8:
static<E> E getRandomSetElement(Set<E> set){return set.stream().skip(newRandom().nextInt(set.size())).findFirst().orElse(null);}
Set<Integer> set =newLinkedHashSet<Integer>(3);
set.add(1);
set.add(2);
set.add(3);Random rand =newRandom(System.currentTimeMillis());int[] setArray =(int[]) set.toArray();for(int i =0; i <10;++i){System.out.println(setArray[rand.nextInt(set.size())]);}
To jest strasznie nieefektywne. Konstruktor ArrayList wywołuje .toArray () na podanym zestawie. ToArray (w większości, jeśli nie we wszystkich standardowych implementacjach kolekcji) wykonuje iterację po całej kolekcji, wypełniając tablicę na bieżąco. Następnie tasujesz listę, co powoduje zamianę każdego elementu na losowy. Byłoby znacznie lepiej, gdybyś po prostu iterował zestaw do losowego elementu.
Chris Bode
4
Jest to identyczne z zaakceptowaną odpowiedzią (Khoth), ale z niepotrzebną sizei usuniętymi i zmiennymi.
int random =newRandom().nextInt(myhashSet.size());for(Object obj : myhashSet){if(random--==0){return obj;}}
Pomimo rezygnacji z dwóch wyżej wymienionych zmiennych, powyższe rozwiązanie nadal pozostaje przypadkowe, ponieważ polegamy na losowym (zaczynającym się od losowo wybranym indeksie), aby zmniejszyć się w kierunku 0każdej iteracji.
C ++. Powinno to być dość szybkie, ponieważ nie wymaga iterowania po całym zestawie ani sortowania go. Powinno to działać po wyjęciu z pudełka z większością nowoczesnych kompilatorów, zakładając, że obsługują one tr1 . Jeśli nie, może być konieczne użycie funkcji Boost.
Dokumentacja Boost są tutaj pomocne, aby to wyjaśnić, nawet jeśli nie używasz Boost.
Sztuczka polega na wykorzystaniu faktu, że dane zostały podzielone na kosze i szybko zidentyfikować losowo wybrany kosz (z odpowiednim prawdopodobieństwem).
//#include <boost/unordered_set.hpp> //using namespace boost;#include <tr1/unordered_set>
using namespace std::tr1;#include <iostream>#include <stdlib.h>#include <assert.h>
using namespace std;int main(){
unordered_set<int> u;
u.max_load_factor(40);for(int i=0; i<40; i++){
u.insert(i);
cout <<' '<< i;}
cout << endl;
cout <<"Number of buckets: "<< u.bucket_count()<< endl;for(size_t b=0; b<u.bucket_count(); b++)
cout <<"Bucket "<< b <<" has "<< u.bucket_size(b)<<" elements. "<< endl;for(size_t i=0; i<20; i++){size_t x = rand()% u.size();
cout <<"we'll quickly get the "<< x <<"th item in the unordered set. ";size_t b;for(b=0; b<u.bucket_count(); b++){if(x < u.bucket_size(b)){break;}else
x -= u.bucket_size(b);}
cout <<"it'll be in the "<< b <<"th bucket at offset "<< x <<". ";
unordered_set<int>::const_local_iterator l = u.begin(b);while(x>0){
l++;assert(l!=u.end(b));
x--;}
cout <<"random item is "<<*l <<". ";
cout << endl;}}
Powyższe rozwiązanie mówi w kategoriach opóźnienia, ale nie gwarantuje równego prawdopodobieństwa wybrania każdego indeksu.
Jeśli trzeba to wziąć pod uwagę, spróbuj pobrać próbki ze zbiornika. http://en.wikipedia.org/wiki/Reservoir_sampling . Collections.shuffle () (jak sugeruje niewielu) używa jednego z takich algorytmów.
Tyle, że [1, 2, 3, 4, 5, 6] nie jest zbiorem, ale listą, ponieważ nie obsługuje takich rzeczy jak szybkie wyszukiwanie.
Thomas Ahle
Nadal możesz: >>> random.choice (list (set (range (5)))) >>> 4 Nie jest to idealne, ale wystarczy, jeśli absolutnie tego potrzebujesz.
Sapphire
1
Nie możesz po prostu uzyskać rozmiaru / długości zestawu / tablicy, wygenerować losową liczbę od 0 do rozmiaru / długości, a następnie wywołać element, którego indeks pasuje do tej liczby? HashSet ma metodę .size (), jestem prawie pewien.
W psuedokodzie -
function randFromSet(target){
var targetLength:uint = target.length()
var randomIndex:uint = random(0,targetLength);return target[randomIndex];}
Działa to tylko wtedy, gdy dany kontener obsługuje losowe wyszukiwanie indeksu. Wiele implementacji kontenerów tego nie robi (np. Tabele skrótów, drzewa binarne, połączone listy).
Random random =newRandom((int)DateTime.Now.Ticks);OrderedDictionary od =newOrderedDictionary();
od.Add("abc",1);
od.Add("def",2);
od.Add("ghi",3);
od.Add("jkl",4);int randomIndex = random.Next(od.Count);Console.WriteLine(od[randomIndex]);// Can access via index or key value:Console.WriteLine(od[1]);Console.WriteLine(od["def"]);
wygląda na to, że odrzucili głosy, ponieważ do kiepskiego słownika java (lub tak zwanego LinkedHashSet, cokolwiek to jest, do diabła) nie można uzyskać „losowego dostępu” (do którego można uzyskać dostęp za pomocą klucza). Gówno java tak bardzo mnie rozśmiesza
Federico Berasategui
1
Rozwiązanie Javascript;)
function choose (set){return set[Math.floor(Math.random()* set.length)];}
var set =[1,2,3,4], rand = choose (set);
Lub alternatywnie:
Array.prototype.choose = function (){returnthis[Math.floor(Math.random()*this.length)];};[1,2,3,4].choose();
Działa to tylko w przypadku list, prawda? Dzięki ELTtemu może pracować dla dowolnej sekwencji.
Ken,
1
W Mathematica:
a ={1,2,3,4,5}
a[[⌈Length[a]Random[]⌉]]
Lub, w najnowszych wersjach, po prostu:
RandomChoice[a]
Ta otrzymała głos negatywny, być może dlatego, że brakuje jej wyjaśnienia, więc oto jedno:
Random[]generuje pseudolosową liczbę zmiennoprzecinkową z przedziału od 0 do 1. Jest to mnożone przez długość listy, a następnie funkcja sufitu jest używana do zaokrąglania w górę do następnej liczby całkowitej. Ten indeks jest następnie wyodrębniany z a.
Ponieważ funkcja tablicy skrótów jest często wykonywana za pomocą reguł w Mathematica, a reguły są przechowywane na listach, można użyć:
a ={"Badger"->5,"Bird"->1,"Fox"->3,"Frog"->2,"Wolf"->4};
Dla zabawy napisałem RandomHashSet na podstawie próbkowania odrzucania. To trochę hacky, ponieważ HashMap nie pozwala nam uzyskać bezpośredniego dostępu do jego tabeli, ale powinno działać dobrze.
Nie używa dodatkowej pamięci, a czas wyszukiwania jest amortyzowany O (1). (Ponieważ java HashTable jest gęsty).
Dokładnie to, o czym myślałem! Najlepsza odpowiedź!
mmm
Właściwie wracając do tego, myślę, że to nie jest całkiem jednolite, jeśli hashmap ma wiele kolizji i robimy wiele zapytań. Dzieje się tak, ponieważ hashmap java używa segmentów / łańcuchów, a ten kod zawsze zwróci pierwszy element w określonym zasobniku. Nadal jednak jesteśmy jednorodni co do losowości funkcji skrótu.
Z guawą możemy zrobić trochę lepiej niż odpowiedź Khotha:
publicstatic E random(Set<E> set){int index = random.nextInt(set.size();if(set instanceofImmutableSet){// ImmutableSet.asList() is O(1), as is .get() on the returned listreturn set.asList().get(index);}returnIterables.get(set, index);}
możesz również przenieść zestaw do tablicy użyj tablicy to prawdopodobnie zadziała na małą skalę Widzę pętlę for w najczęściej głosowanej odpowiedzi i tak O (n)
Object[] arr = set.toArray();int v =(int) arr[rnd.nextInt(arr.length)];
Jeśli naprawdę chcesz po prostu wybrać „dowolny” obiekt z elementu Set, bez żadnych gwarancji co do losowości, najłatwiej jest wziąć pierwszy zwrócony przez iterator.
Set<Integer> s =...Iterator<Integer> it = s.iterator();if(it.hasNext()){Integer i = it.next();// i is a "random" object from set}
Nie będzie to jednak losowy wybór. Wyobraź sobie wielokrotne wykonywanie tej samej operacji na tym samym zestawie. Myślę, że kolejność będzie taka sama.
Menezes Sousa
0
Ogólne rozwiązanie wykorzystujące odpowiedź Khotha jako punkt wyjścia.
/**
* @param set a Set in which to look for a random element
* @param <T> generic type of the Set elements
* @return a random element in the Set or null if the set is empty
*/public<T> T randomElement(Set<T> set){int size = set.size();int item = random.nextInt(size);int i =0;for(T obj : set){if(i == item){return obj;}
i++;}returnnull;}
Niestety, nie można tego zrobić wydajnie (lepiej niż O (n)) w żadnym z kontenerów zestawu biblioteki standardowej.
Jest to dziwne, ponieważ bardzo łatwo jest dodać losową funkcję wybierania zarówno do zestawów haszujących, jak i binarnych. W niezbyt rzadkim zestawie skrótów możesz próbować losowych wpisów, aż uzyskasz trafienie. W przypadku drzewa binarnego można losowo wybierać między lewym lub prawym poddrzewem, z maksymalną liczbą O (log2) kroków. Zaimplementowałem demo poniżej:
import random
classNode:
def __init__(self, object):
self.object = object
self.value = hash(object)
self.size =1
self.a = self.b =NoneclassRandomSet:
def __init__(self):
self.top =None
def add(self, object):"""Add any hashable object to the set.Notice:Inthis simple implementation you shouldn't add two
identical items."""new=Node(object)if not self.top: self.top =newelse: self._recursiveAdd(self.top,new)
def _recursiveAdd(self, top,new):
top.size +=1ifnew.value < top.value:if not top.a: top.a =newelse: self._recursiveAdd(top.a,new)else:if not top.b: top.b =newelse: self._recursiveAdd(top.b,new)
def pickRandom(self):"""Pick a random item in O(log2) time.Does a maximum of O(log2) calls to random as well."""return self._recursivePickRandom(self.top)
def _recursivePickRandom(self, top):
r = random.randrange(top.size)if r ==0:return top.object
elif top.a and r <= top.a.size:return self._recursivePickRandom(top.a)return self._recursivePickRandom(top.b)if __name__ =='__main__':
s =RandomSet()for i in [5,3,7,1,4,6,9,2,8,0]:
s.add(i)
dists =[0]*10for i in xrange(10000):
dists[s.pickRandom()]+=1
print dists
Otrzymałem [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] jako wyjście, więc rozkład wydaje się dobry.
Sam zmagałem się z tym samym problemem i nie zdecydowałem jeszcze, czy wzrost wydajności tego bardziej wydajnego wyboru jest wart kosztów narzutów związanych z używaniem kolekcji opartej na Pythonie. Mógłbym to oczywiście udoskonalić i przetłumaczyć na C, ale to dla mnie dziś za dużo pracy :)
Myślę, że powodem, dla którego nie jest to zaimplementowane w drzewie binarnym, jest to, że taka metoda nie wybierałaby elementów w sposób jednolity. Ponieważ są to węzły bez lewych / prawych dzieci, może wystąpić sytuacja, w której lewe dziecko zawiera więcej elementów niż prawe dziecko (lub odwrotnie), co spowodowałoby, że wybranie elementu po prawej (lub lewej) stronie dziecka byłoby bardziej prawdopodobne.
Willem Van Onsem
1
@CommuSoft: Dlatego przechowuję rozmiar każdego poddrzewa, więc mogę wybrać moje prawdopodobieństwa na podstawie tych.
Odpowiedzi:
źródło
Nieco pokrewne Czy wiesz, że:
Istnieją przydatne metody
java.util.Collections
tasowania całych kolekcji:Collections.shuffle(List<?>)
iCollections.shuffle(List<?> list, Random rnd)
.źródło
List
interfejs, a nie dlaSet
interfejsu omawianego przez OP.Szybkie rozwiązanie dla języka Java za pomocą znaków
ArrayList
iHashMap
: [element -> indeks].Motywacja: potrzebowałem zestawu przedmiotów z
RandomAccess
właściwościami, zwłaszcza aby wybrać losowy przedmiot z zestawu (patrzpollRandom
metoda). Losowa nawigacja w drzewie binarnym nie jest dokładna: drzewa nie są idealnie zrównoważone, co nie prowadziłoby do równomiernego rozmieszczenia.źródło
Concurrent
są naprawdę bezpieczne, te owinięteCollections.synchronized()
są pół-bezpieczne. Również OP nie powiedział nic o współbieżności, więc jest to poprawna i dobra odpowiedź.dta
(można to osiągnąćIterators.unmodifiableIterator
na przykład za pomocą guawy ). W przeciwnym razie domyślne implementacje, np. RemoveAll i retainAll w AbstractSet i jego elementy nadrzędne pracujące z tym iteratorem, zepsująRandomSet
!Jest to szybsze niż pętla for-each w akceptowanej odpowiedzi:
Konstrukcja for-each wywołuje
Iterator.hasNext()
każdą pętlę, ale ponieważindex < set.size()
to sprawdzenie jest niepotrzebnym narzutem. Widziałem wzrost prędkości o 10-20%, ale YMMV. (Ponadto kompiluje się bez konieczności dodawania dodatkowej instrukcji return).Zwróć uwagę, że ten kod (i większość innych odpowiedzi) można zastosować do dowolnej kolekcji, a nie tylko do zestawu. W ogólnej formie metody:
źródło
Jeśli chcesz to zrobić w Javie, powinieneś rozważyć skopiowanie elementów do jakiejś kolekcji o swobodnym dostępie (takiej jak ArrayList). Ponieważ, chyba że twój zestaw jest mały, dostęp do wybranego elementu będzie kosztowny (O (n) zamiast O (1)). [ed: kopia listy jest również O (n)]
Alternatywnie możesz poszukać innej implementacji zestawu, która bardziej odpowiada Twoim wymaganiom. Zestaw ListOrderedSet z Commons Collections wygląda obiecująco.
źródło
W Javie 8:
źródło
W Javie:
źródło
źródło
Jest to identyczne z zaakceptowaną odpowiedzią (Khoth), ale z niepotrzebną
size
i
usuniętymi i zmiennymi.Pomimo rezygnacji z dwóch wyżej wymienionych zmiennych, powyższe rozwiązanie nadal pozostaje przypadkowe, ponieważ polegamy na losowym (zaczynającym się od losowo wybranym indeksie), aby zmniejszyć się w kierunku
0
każdej iteracji.źródło
if (--random < 0) {
, gdzierandom
sięga-1
.Rozwiązanie Clojure:
źródło
nth
elementu, musisz również przejść przez niegoseq
.Perl 5
Oto jeden sposób, aby to zrobić.
źródło
C ++. Powinno to być dość szybkie, ponieważ nie wymaga iterowania po całym zestawie ani sortowania go. Powinno to działać po wyjęciu z pudełka z większością nowoczesnych kompilatorów, zakładając, że obsługują one tr1 . Jeśli nie, może być konieczne użycie funkcji Boost.
Dokumentacja Boost są tutaj pomocne, aby to wyjaśnić, nawet jeśli nie używasz Boost.
Sztuczka polega na wykorzystaniu faktu, że dane zostały podzielone na kosze i szybko zidentyfikować losowo wybrany kosz (z odpowiednim prawdopodobieństwem).
źródło
Powyższe rozwiązanie mówi w kategoriach opóźnienia, ale nie gwarantuje równego prawdopodobieństwa wybrania każdego indeksu.
Jeśli trzeba to wziąć pod uwagę, spróbuj pobrać próbki ze zbiornika. http://en.wikipedia.org/wiki/Reservoir_sampling .
Collections.shuffle () (jak sugeruje niewielu) używa jednego z takich algorytmów.
źródło
Ponieważ powiedziałeś „Mile widziane są również rozwiązania dla innych języków”, oto wersja dla Pythona:
źródło
Nie możesz po prostu uzyskać rozmiaru / długości zestawu / tablicy, wygenerować losową liczbę od 0 do rozmiaru / długości, a następnie wywołać element, którego indeks pasuje do tej liczby? HashSet ma metodę .size (), jestem prawie pewien.
W psuedokodzie -
źródło
PHP, zakładając, że „zestaw” jest tablicą:
Funkcje Mersenne Twister są lepsze, ale w PHP nie ma odpowiednika MT dla array_rand.
źródło
Ikona ma ustawiony typ i operator elementu losowego, jednoargumentowy „?”, Czyli wyrażenie
wygeneruje losową liczbę od 1 do 5.
Losowe ziarno jest inicjowane na 0, gdy program jest uruchamiany, aby uzyskać różne wyniki przy każdym uruchomieniu
randomize()
źródło
W C #
źródło
Rozwiązanie Javascript;)
Lub alternatywnie:
źródło
W seplenieniu
źródło
ELT
temu może pracować dla dowolnej sekwencji.W Mathematica:
Lub, w najnowszych wersjach, po prostu:
Ta otrzymała głos negatywny, być może dlatego, że brakuje jej wyjaśnienia, więc oto jedno:
Random[]
generuje pseudolosową liczbę zmiennoprzecinkową z przedziału od 0 do 1. Jest to mnożone przez długość listy, a następnie funkcja sufitu jest używana do zaokrąglania w górę do następnej liczby całkowitej. Ten indeks jest następnie wyodrębniany za
.Ponieważ funkcja tablicy skrótów jest często wykonywana za pomocą reguł w Mathematica, a reguły są przechowywane na listach, można użyć:
źródło
Może po prostu
źródło
Dla zabawy napisałem RandomHashSet na podstawie próbkowania odrzucania. To trochę hacky, ponieważ HashMap nie pozwala nam uzyskać bezpośredniego dostępu do jego tabeli, ale powinno działać dobrze.
Nie używa dodatkowej pamięci, a czas wyszukiwania jest amortyzowany O (1). (Ponieważ java HashTable jest gęsty).
źródło
Najłatwiejszy z Javą 8 to:
gdzie
n
jest losową liczbą całkowitą. Oczywiście ma mniejszą wydajność niż w przypadkufor(elem: Col)
źródło
Z guawą możemy zrobić trochę lepiej niż odpowiedź Khotha:
źródło
PHP, używając MT:
źródło
możesz również przenieść zestaw do tablicy użyj tablicy to prawdopodobnie zadziała na małą skalę Widzę pętlę for w najczęściej głosowanej odpowiedzi i tak O (n)
źródło
Jeśli naprawdę chcesz po prostu wybrać „dowolny” obiekt z elementu
Set
, bez żadnych gwarancji co do losowości, najłatwiej jest wziąć pierwszy zwrócony przez iterator.źródło
Ogólne rozwiązanie wykorzystujące odpowiedź Khotha jako punkt wyjścia.
źródło
Niestety, nie można tego zrobić wydajnie (lepiej niż O (n)) w żadnym z kontenerów zestawu biblioteki standardowej.
Jest to dziwne, ponieważ bardzo łatwo jest dodać losową funkcję wybierania zarówno do zestawów haszujących, jak i binarnych. W niezbyt rzadkim zestawie skrótów możesz próbować losowych wpisów, aż uzyskasz trafienie. W przypadku drzewa binarnego można losowo wybierać między lewym lub prawym poddrzewem, z maksymalną liczbą O (log2) kroków. Zaimplementowałem demo poniżej:
Otrzymałem [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] jako wyjście, więc rozkład wydaje się dobry.
Sam zmagałem się z tym samym problemem i nie zdecydowałem jeszcze, czy wzrost wydajności tego bardziej wydajnego wyboru jest wart kosztów narzutów związanych z używaniem kolekcji opartej na Pythonie. Mógłbym to oczywiście udoskonalić i przetłumaczyć na C, ale to dla mnie dziś za dużo pracy :)
źródło