¿Cómo elijo un elemento aleatorio de un conjunto? Estoy particularmente interesado en elegir un elemento aleatorio de un HashSet o LinkedHashSet, en Java. Las soluciones para otros idiomas también son bienvenidas.
Debe especificar algunas condiciones para ver si esto es realmente lo que desea. - ¿Cuántas veces vas a seleccionar un elemento aleatorio? - ¿Es necesario almacenar los datos en un HashSet o LinkedHashSet? No se puede acceder a ninguno de ellos de manera aleatoria. - ¿Es grande el hash? ¿Son pequeñas las llaves?
David Nehme
Respuestas:
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++;}
Si myHashSet es grande, entonces esta será una solución bastante lenta ya que, en promedio, se necesitarán (n / 2) iteraciones para encontrar el objeto aleatorio.
daniel
66
Si sus datos están en un conjunto hash, necesita tiempo O (n). No hay forma de evitarlo si solo está eligiendo un solo elemento y los datos se almacenan en un HashSet.
David Nehme
8
@David Nehme: este es un inconveniente en la especificación de HashSet en Java. En C ++, es típico poder acceder directamente a los cubos que componen el hashset, lo que nos permite seleccionar de manera más eficiente elementos aleatorios. Si los elementos aleatorios son necesarios en Java, podría valer la pena definir un conjunto de hash personalizado que permita al usuario mirar debajo del capó. Ver [documentos de impulso] [1] para un poco más en esto. [1] boost.org/doc/libs/1_43_0/doc/html/unordered/buckets.html
Aaron McDaid el
11
Si el conjunto no está mutado en múltiples accesos, puede copiarlo en una matriz y luego acceder a O (1). Solo use myHashSet.toArray ()
ykaganovich el
2
@ykaganovich, ¿esto no empeoraría las cosas, ya que el conjunto tendría que copiarse en una nueva matriz? docs.oracle.com/javase/7/docs/api/java/util/… "este método debe asignar una nueva matriz incluso si esta colección está respaldada por una matriz"
¡Increíble! ¡Esto no tiene referencias cruzadas en ningún lugar del documento de Java! Al igual que random.shuffle de Python ()
smci
25
Pero esto solo funciona con Listas, es decir, estructuras que tienen una función .get ().
bourbaki4481472
44
@ bourbaki4481472 es absolutamente correcto. Esto solo funciona para aquellas colecciones que extienden la Listinterfaz, no la Setinterfaz discutida por el OP.
Thomas
31
Solución rápida para Java utilizando una ArrayListy una HashMap: [elemento -> índice].
Motivación: necesitaba un conjunto de elementos con RandomAccesspropiedades, especialmente para elegir un elemento aleatorio del conjunto (ver pollRandommétodo). La navegación aleatoria en un árbol binario no es precisa: los árboles no están perfectamente equilibrados, lo que no conduciría a una distribución uniforme.
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();}}
Bueno, eso funcionaría, pero la pregunta era sobre la interfaz Set. Esta solución obliga a los usuarios a tener referencias de tipos concretos de RandomSet.
Johan Tidén
Realmente me gusta esta solución, pero no es segura para subprocesos, pueden ocurrir imprecisiones entre el Mapa y la Lista, por lo que agregaría algunos bloques sincronizados
Kostas Chalkias
@KonstantinosChalkias las colecciones integradas tampoco son seguras para subprocesos. Solo los que tienen el nombre Concurrentson realmente seguros, los que están envueltos Collections.synchronized()son semi-seguros. Además, el OP no dijo nada sobre la concurrencia, por lo que esta es una respuesta válida y buena.
TWiStErRob
El iterador devuelto aquí no debería poder eliminar elementos de dta(esto se puede lograr a través de guayaba, Iterators.unmodifiableIteratorpor ejemplo). De lo contrario, las implementaciones predeterminadas de, por ejemplo, removeAll y retenerAll en AbstractSet y sus padres que trabajan con ese iterador lo arruinarán RandomSet.
silenciado el
Buena solución En realidad, puede usar un árbol si cada nodo contiene el número de nodos en el subárbol que arraiga. Luego calcule un real aleatorio en 0..1 y tome una decisión de 3 vías ponderada (seleccione el nodo actual o descienda al subárbol izquierdo o derecho) en cada nodo en función de los recuentos de nodos. Pero mi solución es mucho mejor.
Gene
29
Esto es más rápido que el ciclo for-each en la respuesta aceptada:
int index = rand.nextInt(set.size());Iterator<Object> iter = set.iterator();for(int i =0; i < index; i++){
iter.next();}return iter.next();
La construcción for-each llama Iterator.hasNext()a cada ciclo, pero desde entonces index < set.size(), esa verificación es una sobrecarga innecesaria. Vi un aumento del 10-20% en la velocidad, pero YMMV. (Además, esto se compila sin tener que agregar una declaración de devolución adicional).
Tenga en cuenta que este código (y la mayoría de las otras respuestas) se pueden aplicar a cualquier Colección, no solo a Establecer. En forma de método genérico:
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();}}
Si desea hacerlo en Java, debería considerar copiar los elementos en algún tipo de colección de acceso aleatorio (como una ArrayList). Porque, a menos que su conjunto sea pequeño, acceder al elemento seleccionado será costoso (O (n) en lugar de O (1)). [ed: la copia de la lista también es O (n)]
Alternativamente, puede buscar otra implementación de Set que coincida más estrechamente con sus requisitos. El ListOrderedSet de Commons Collections parece prometedor.
Copiar a una lista le costará O (n) a tiempo y también usará memoria O (n), entonces, ¿por qué sería una mejor opción que buscar directamente del mapa?
mdma
12
Depende de cuántas veces desee elegir del conjunto. La copia es una operación única y luego puede elegir del conjunto tantas veces como sea necesario. Si solo está eligiendo un elemento, entonces sí, la copia no hace las cosas más rápido.
Dan Dyer
Es solo una operación de una sola vez si desea poder elegir con repetición. Si desea que el elemento elegido se elimine del conjunto, volvería a O (n).
TurnipEntropy
12
En Java 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())]);}
Esto es abismalmente ineficiente. Su constructor ArrayList llama a .toArray () en el conjunto suministrado. ToArray (en la mayoría, si no en todas las implementaciones de colección estándar) itera sobre toda la colección, llenando una matriz a medida que avanza. Luego baraja la lista, que intercambia cada elemento con un elemento aleatorio. Sería mucho mejor simplemente iterar sobre el conjunto a un elemento aleatorio.
Chris Bode
4
Esto es idéntico a la respuesta aceptada (Khoth), pero con lo innecesario sizey las ivariables eliminadas.
int random =newRandom().nextInt(myhashSet.size());for(Object obj : myhashSet){if(random--==0){return obj;}}
Aunque eliminando las dos variables mencionadas anteriormente, la solución anterior sigue siendo aleatoria porque confiamos en aleatoria (comenzando en un índice seleccionado aleatoriamente) para disminuir hacia 0cada iteración.
C ++. Esto debería ser razonablemente rápido, ya que no requiere iterar en todo el conjunto u ordenarlo. Esto debería funcionar de inmediato con la mayoría de los compiladores modernos, suponiendo que sean compatibles con tr1 . Si no, es posible que deba usar Boost.
Los documentos de Boost son útiles aquí para explicar esto, incluso si no usa Boost.
El truco consiste en hacer uso del hecho de que los datos se han dividido en cubos e identificar rápidamente un cubo elegido al azar (con la probabilidad adecuada).
//#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;}}
La solución anterior habla en términos de latencia, pero no garantiza la misma probabilidad de que se seleccione cada índice.
Si eso necesita ser considerado, pruebe el muestreo de yacimientos. http://en.wikipedia.org/wiki/Reservoir_sampling . Collections.shuffle () (como sugieren algunos) usa uno de esos algoritmos.
Solo que [1,2,3,4,5,6] no es un conjunto, sino una lista, ya que no admite cosas como búsquedas rápidas.
Thomas Ahle
Todavía puede hacer: >>> random.choice (list (set (range (5)))) >>> 4 No es ideal, pero lo hará si es absolutamente necesario.
SapphireSun
1
¿No puede obtener el tamaño / longitud del conjunto / matriz, generar un número aleatorio entre 0 y el tamaño / longitud, y luego llamar al elemento cuyo índice coincide con ese número? HashSet tiene un método .size (), estoy bastante seguro.
En psuedocode -
function randFromSet(target){
var targetLength:uint = target.length()
var randomIndex:uint = random(0,targetLength);return target[randomIndex];}
Esto solo funciona si el contenedor en cuestión admite la búsqueda de índice aleatorio. Muchas implementaciones de contenedores no lo hacen (por ejemplo, tablas hash, árboles binarios, listas vinculadas).
La mayoría de las implementaciones establecidas no tienen un operador get (i) o de indexación, así que supongo que es por eso que OP especificó su conjunto
DownloadPizza
1
El icono tiene un tipo de conjunto y un operador de elemento aleatorio, unario "?", Por lo que la expresión
? set([1,2,3,4,5])
producirá un número aleatorio entre 1 y 5.
La inicialización aleatoria se inicializa a 0 cuando se ejecuta un programa, por lo que para producir resultados diferentes en cada ejecución, use randomize()
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"]);
parece que se votó en contra porque el diccionario de Java de mierda (o el llamado LinkedHashSet, lo que sea que sea) no se puede "acceder aleatoriamente" (al que se accede por clave, supongo). La mierda de Java me hace reír mucho
Federico Berasategui
1
Solución Javascript;)
function choose (set){return set[Math.floor(Math.random()* set.length)];}
var set =[1,2,3,4], rand = choose (set);
O alternativamente:
Array.prototype.choose = function (){returnthis[Math.floor(Math.random()*this.length)];};[1,2,3,4].choose();
Esto solo funciona para listas, ¿verdad? Con ELTeso podría funcionar para cualquier secuencia.
Ken
1
En Mathematica:
a ={1,2,3,4,5}
a[[⌈Length[a]Random[]⌉]]
O, en versiones recientes, simplemente:
RandomChoice[a]
Esto recibió un voto negativo, tal vez porque carece de explicación, así que aquí hay uno:
Random[]genera un flotante pseudoaleatorio entre 0 y 1. Esto se multiplica por la longitud de la lista y luego la función de techo se utiliza para redondear al siguiente entero. Este índice se extrae de a.
Dado que la funcionalidad de la tabla hash se realiza con frecuencia con reglas en Mathematica, y las reglas se almacenan en listas, se podría usar:
a ={"Badger"->5,"Bird"->1,"Fox"->3,"Frog"->2,"Wolf"->4};
Por diversión, escribí un RandomHashSet basado en muestras de rechazo. Es un poco hacky, ya que HashMap no nos permite acceder a su tabla directamente, pero debería funcionar bien.
No utiliza memoria adicional, y el tiempo de búsqueda es O (1) amortizado. (Porque Java HashTable es denso).
¡Exactamente lo que estaba pensando! ¡La mejor respuesta!
mmm
En realidad, volviendo a eso, supongo que esto no es bastante uniforme, si el hashmap tiene muchas colisiones y hacemos muchas consultas. Esto se debe a que el hashmap de Java usa cubos / encadenamiento y este código siempre devolverá el primer elemento en el cubo particular. Sin embargo, todavía somos uniformes sobre la aleatoriedad de la función hash.
Con Guava podemos hacer un poco mejor que la respuesta de Khoth:
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);}
también puede transferir el conjunto a la matriz usar matriz, probablemente funcionará a pequeña escala, veo que el bucle for en la respuesta más votada es O (n) de todos modos
Object[] arr = set.toArray();int v =(int) arr[rnd.nextInt(arr.length)];
Si realmente solo desea elegir "cualquier" objeto del Set, sin ninguna garantía sobre la aleatoriedad, lo más fácil es tomar el primero devuelto por el iterador.
Set<Integer> s =...Iterator<Integer> it = s.iterator();if(it.hasNext()){Integer i = it.next();// i is a "random" object from set}
Sin embargo, esta no será una elección aleatoria. Imagine realizar la misma operación en el mismo conjunto varias veces. Creo que el orden será el mismo.
Menezes Sousa
0
Una solución genérica que utiliza la respuesta de Khoth como punto de partida.
/**
* @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;}
Desafortunadamente, esto no se puede hacer de manera eficiente (mejor que O (n)) en ninguno de los contenedores de conjuntos de la Biblioteca estándar.
Esto es extraño, ya que es muy fácil agregar una función de selección aleatoria a conjuntos de hash y conjuntos binarios. En un conjunto de hash no escaso, puede intentar entradas aleatorias, hasta que obtenga un acierto. Para un árbol binario, puede elegir aleatoriamente entre el subárbol izquierdo o derecho, con un máximo de O (log2) pasos. He implementado una demostración de lo siguiente a continuación:
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
Obtuve [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] como salida, por lo que la distribución parece buena.
He luchado con el mismo problema para mí, y aún no he decidido si el aumento de rendimiento de esta elección más eficiente vale la pena de usar una colección basada en Python. Por supuesto, podría refinarlo y traducirlo a C, pero eso es demasiado trabajo para mí hoy :)
Una razón por la que creo que esto no se implementa en un árbol binario es que dicho método no seleccionaría elementos de manera uniforme. Dado que son nodos sin hijos izquierdo / derecho, puede ocurrir una situación en la que el hijo izquierdo contiene más elementos que el hijo derecho (o viceversa), esto haría que elegir un elemento en el hijo derecho (o izquierdo) sea más probable.
Willem Van Onsem
1
@CommuSoft: por eso almaceno el tamaño de cada subárbol, para poder elegir mis probabilidades en función de ellas.
Respuestas:
fuente
Un poco relacionado ¿Sabía usted:
Existen métodos útiles
java.util.Collections
para barajar colecciones enteras:Collections.shuffle(List<?>)
yCollections.shuffle(List<?> list, Random rnd)
.fuente
List
interfaz, no laSet
interfaz discutida por el OP.Solución rápida para Java utilizando una
ArrayList
y unaHashMap
: [elemento -> índice].Motivación: necesitaba un conjunto de elementos con
RandomAccess
propiedades, especialmente para elegir un elemento aleatorio del conjunto (verpollRandom
método). La navegación aleatoria en un árbol binario no es precisa: los árboles no están perfectamente equilibrados, lo que no conduciría a una distribución uniforme.fuente
Concurrent
son realmente seguros, los que están envueltosCollections.synchronized()
son semi-seguros. Además, el OP no dijo nada sobre la concurrencia, por lo que esta es una respuesta válida y buena.dta
(esto se puede lograr a través de guayaba,Iterators.unmodifiableIterator
por ejemplo). De lo contrario, las implementaciones predeterminadas de, por ejemplo, removeAll y retenerAll en AbstractSet y sus padres que trabajan con ese iterador lo arruinaránRandomSet
.Esto es más rápido que el ciclo for-each en la respuesta aceptada:
La construcción for-each llama
Iterator.hasNext()
a cada ciclo, pero desde entoncesindex < set.size()
, esa verificación es una sobrecarga innecesaria. Vi un aumento del 10-20% en la velocidad, pero YMMV. (Además, esto se compila sin tener que agregar una declaración de devolución adicional).Tenga en cuenta que este código (y la mayoría de las otras respuestas) se pueden aplicar a cualquier Colección, no solo a Establecer. En forma de método genérico:
fuente
Si desea hacerlo en Java, debería considerar copiar los elementos en algún tipo de colección de acceso aleatorio (como una ArrayList). Porque, a menos que su conjunto sea pequeño, acceder al elemento seleccionado será costoso (O (n) en lugar de O (1)). [ed: la copia de la lista también es O (n)]
Alternativamente, puede buscar otra implementación de Set que coincida más estrechamente con sus requisitos. El ListOrderedSet de Commons Collections parece prometedor.
fuente
En Java 8:
fuente
En Java:
fuente
fuente
Esto es idéntico a la respuesta aceptada (Khoth), pero con lo innecesario
size
y lasi
variables eliminadas.Aunque eliminando las dos variables mencionadas anteriormente, la solución anterior sigue siendo aleatoria porque confiamos en aleatoria (comenzando en un índice seleccionado aleatoriamente) para disminuir hacia
0
cada iteración.fuente
if (--random < 0) {
, donderandom
llega-1
.Solución Clojure:
fuente
nth
elemento también debe atravesarloseq
.Perl 5
Aquí hay una forma de hacerlo.
fuente
C ++. Esto debería ser razonablemente rápido, ya que no requiere iterar en todo el conjunto u ordenarlo. Esto debería funcionar de inmediato con la mayoría de los compiladores modernos, suponiendo que sean compatibles con tr1 . Si no, es posible que deba usar Boost.
Los documentos de Boost son útiles aquí para explicar esto, incluso si no usa Boost.
El truco consiste en hacer uso del hecho de que los datos se han dividido en cubos e identificar rápidamente un cubo elegido al azar (con la probabilidad adecuada).
fuente
La solución anterior habla en términos de latencia, pero no garantiza la misma probabilidad de que se seleccione cada índice.
Si eso necesita ser considerado, pruebe el muestreo de yacimientos. http://en.wikipedia.org/wiki/Reservoir_sampling .
Collections.shuffle () (como sugieren algunos) usa uno de esos algoritmos.
fuente
Como dijiste "Las soluciones para otros idiomas también son bienvenidas", aquí está la versión para Python:
fuente
¿No puede obtener el tamaño / longitud del conjunto / matriz, generar un número aleatorio entre 0 y el tamaño / longitud, y luego llamar al elemento cuyo índice coincide con ese número? HashSet tiene un método .size (), estoy bastante seguro.
En psuedocode -
fuente
PHP, suponiendo que "set" es una matriz:
Las funciones de Mersenne Twister son mejores, pero no hay un equivalente MT de array_rand en PHP.
fuente
El icono tiene un tipo de conjunto y un operador de elemento aleatorio, unario "?", Por lo que la expresión
producirá un número aleatorio entre 1 y 5.
La inicialización aleatoria se inicializa a 0 cuando se ejecuta un programa, por lo que para producir resultados diferentes en cada ejecución, use
randomize()
fuente
C ª#
fuente
Solución Javascript;)
O alternativamente:
fuente
En lisp
fuente
ELT
eso podría funcionar para cualquier secuencia.En Mathematica:
O, en versiones recientes, simplemente:
Esto recibió un voto negativo, tal vez porque carece de explicación, así que aquí hay uno:
Random[]
genera un flotante pseudoaleatorio entre 0 y 1. Esto se multiplica por la longitud de la lista y luego la función de techo se utiliza para redondear al siguiente entero. Este índice se extrae dea
.Dado que la funcionalidad de la tabla hash se realiza con frecuencia con reglas en Mathematica, y las reglas se almacenan en listas, se podría usar:
fuente
¿Qué tal solo
fuente
Por diversión, escribí un RandomHashSet basado en muestras de rechazo. Es un poco hacky, ya que HashMap no nos permite acceder a su tabla directamente, pero debería funcionar bien.
No utiliza memoria adicional, y el tiempo de búsqueda es O (1) amortizado. (Porque Java HashTable es denso).
fuente
Lo más fácil con Java 8 es:
donde
n
es un entero aleatorio. Por supuesto, es de menor rendimiento que eso con elfor(elem: Col)
fuente
Con Guava podemos hacer un poco mejor que la respuesta de Khoth:
fuente
PHP, usando MT:
fuente
también puede transferir el conjunto a la matriz usar matriz, probablemente funcionará a pequeña escala, veo que el bucle for en la respuesta más votada es O (n) de todos modos
fuente
Si realmente solo desea elegir "cualquier" objeto del
Set
, sin ninguna garantía sobre la aleatoriedad, lo más fácil es tomar el primero devuelto por el iterador.fuente
Una solución genérica que utiliza la respuesta de Khoth como punto de partida.
fuente
Desafortunadamente, esto no se puede hacer de manera eficiente (mejor que O (n)) en ninguno de los contenedores de conjuntos de la Biblioteca estándar.
Esto es extraño, ya que es muy fácil agregar una función de selección aleatoria a conjuntos de hash y conjuntos binarios. En un conjunto de hash no escaso, puede intentar entradas aleatorias, hasta que obtenga un acierto. Para un árbol binario, puede elegir aleatoriamente entre el subárbol izquierdo o derecho, con un máximo de O (log2) pasos. He implementado una demostración de lo siguiente a continuación:
Obtuve [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] como salida, por lo que la distribución parece buena.
He luchado con el mismo problema para mí, y aún no he decidido si el aumento de rendimiento de esta elección más eficiente vale la pena de usar una colección basada en Python. Por supuesto, podría refinarlo y traducirlo a C, pero eso es demasiado trabajo para mí hoy :)
fuente