मैं एक सेट से एक यादृच्छिक तत्व कैसे चुन सकता हूं? मैं विशेष रूप से जावा में किसी HashSet या LinkedHashSet से एक यादृच्छिक तत्व चुनने में दिलचस्पी रखता हूं। अन्य भाषाओं के समाधान भी स्वागत योग्य हैं।
मैं एक सेट से एक यादृच्छिक तत्व कैसे चुन सकता हूं? मैं विशेष रूप से जावा में किसी HashSet या LinkedHashSet से एक यादृच्छिक तत्व चुनने में दिलचस्पी रखता हूं। अन्य भाषाओं के समाधान भी स्वागत योग्य हैं।
जवाबों:
int size = myHashSet.size();
int item = new Random().nextInt(size); // In real life, the Random object should be rather more shared than this
int i = 0;
for(Object obj : myhashSet)
{
if (i == item)
return obj;
i++;
}
कुछ हद तक संबंधित क्या आप जानते हैं:
java.util.Collections
पूरे संग्रह में फेरबदल के लिए उपयोगी तरीके हैं : Collections.shuffle(List<?>)
और Collections.shuffle(List<?> list, Random rnd)
।
List
इंटरफ़ेस का विस्तार करते हैं , न Set
कि ओपी द्वारा चर्चा किए गए इंटरफ़ेस का।
जावा का तेज़ समाधान ArrayList
a HashMap
: [तत्व -> अनुक्रमणिका] का उपयोग कर रहा है।
प्रेरणा: मुझे RandomAccess
विशेष रूप से सेट से एक यादृच्छिक आइटम लेने के लिए (देखें pollRandom
विधि) गुणों के साथ वस्तुओं के एक सेट की आवश्यकता थी । एक द्विआधारी पेड़ में यादृच्छिक नेविगेशन सटीक नहीं है: पेड़ पूरी तरह से संतुलित नहीं हैं, जिससे एक समान वितरण नहीं होगा।
public class RandomSet<E> extends AbstractSet<E> {
List<E> dta = new ArrayList<E>();
Map<E, Integer> idx = new HashMap<E, Integer>();
public RandomSet() {
}
public RandomSet(Collection<E> items) {
for (E item : items) {
idx.put(item, dta.size());
dta.add(item);
}
}
@Override
public boolean add(E item) {
if (idx.containsKey(item)) {
return false;
}
idx.put(item, dta.size());
dta.add(item);
return true;
}
/**
* Override element at position <code>id</code> with last element.
* @param id
*/
public E removeAt(int id) {
if (id >= dta.size()) {
return null;
}
E res = dta.get(id);
idx.remove(res);
E last = dta.remove(dta.size() - 1);
// skip filling the hole if last is removed
if (id < dta.size()) {
idx.put(last, id);
dta.set(id, last);
}
return res;
}
@Override
public boolean remove(Object item) {
@SuppressWarnings(value = "element-type-mismatch")
Integer id = idx.get(item);
if (id == null) {
return false;
}
removeAt(id);
return true;
}
public E get(int i) {
return dta.get(i);
}
public E pollRandom(Random rnd) {
if (dta.isEmpty()) {
return null;
}
int id = rnd.nextInt(dta.size());
return removeAt(id);
}
@Override
public int size() {
return dta.size();
}
@Override
public Iterator<E> iterator() {
return dta.iterator();
}
}
Concurrent
ही वास्तव में सुरक्षित हैं, जो लपेटे हुए Collections.synchronized()
हैं वे अर्ध-सुरक्षित हैं। साथ ही ओपी ने समसामयिकता के बारे में कुछ नहीं कहा, इसलिए यह एक मान्य और अच्छा जवाब है।
dta
(यह Iterators.unmodifiableIterator
उदाहरण के लिए अमरूद के माध्यम से प्राप्त किया जा सकता है )। अन्यथा AbstractSet में हटाने और सभी को फिर से लागू करने के लिए डिफ़ॉल्ट और AbstractSet और उसके माता पिता के साथ काम कर रहे हैं कि यह आपके गड़बड़ कर देगा RandomSet
!
यह स्वीकृत उत्तर के लिए प्रत्येक लूप से अधिक तेज़ है:
int index = rand.nextInt(set.size());
Iterator<Object> iter = set.iterator();
for (int i = 0; i < index; i++) {
iter.next();
}
return iter.next();
प्रत्येक निर्माण के लिए प्रत्येक Iterator.hasNext()
लूप पर कॉल किया जाता है, लेकिन चूंकि index < set.size()
, चेक अनावश्यक ओवरहेड है। मैंने गति में 10-20% की वृद्धि देखी, लेकिन YMMV। (इसके अलावा, यह अतिरिक्त रिटर्न स्टेटमेंट को जोड़ने के बिना संकलित करता है।)
ध्यान दें कि यह कोड (और अधिकांश अन्य उत्तर) किसी भी संग्रह पर लागू किया जा सकता है, न केवल सेट। सामान्य विधि के रूप में:
public static <E> E choice(Collection<? extends E> coll, Random rand) {
if (coll.size() == 0) {
return null; // or throw IAE, if you prefer
}
int index = rand.nextInt(coll.size());
if (coll instanceof List) { // optimization
return ((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();
}
}
यदि आप इसे जावा में करना चाहते हैं, तो आपको तत्वों को किसी प्रकार के यादृच्छिक-अभिगम संग्रह (जैसे कि एक ArrayList) में कॉपी करने पर विचार करना चाहिए। क्योंकि, जब तक आपका सेट छोटा नहीं होता, तब तक चयनित तत्व तक पहुंचना O (1) के बजाय महंगा (O) होगा। [ed: सूची की प्रतिलिपि भी हे (n)]
वैकल्पिक रूप से, आप एक और सेट कार्यान्वयन की तलाश कर सकते हैं जो आपकी आवश्यकताओं से अधिक निकटता से मेल खाता हो। ListOrderedSet कॉमन्स संग्रह से वादा किया लग।
जावा 8 में:
static <E> E getRandomSetElement(Set<E> set) {
return set.stream().skip(new Random().nextInt(set.size())).findFirst().orElse(null);
}
जावा में:
Set<Integer> set = new LinkedHashSet<Integer>(3);
set.add(1);
set.add(2);
set.add(3);
Random rand = new Random(System.currentTimeMillis());
int[] setArray = (int[]) set.toArray();
for (int i = 0; i < 10; ++i) {
System.out.println(setArray[rand.nextInt(set.size())]);
}
List asList = new ArrayList(mySet);
Collections.shuffle(asList);
return asList.get(0);
यह स्वीकृत उत्तर (खोथ) के समान है, लेकिन अनावश्यक size
और i
चर को हटा दिया गया है।
int random = new Random().nextInt(myhashSet.size());
for(Object obj : myhashSet) {
if (random-- == 0) {
return obj;
}
}
हालांकि उपरोक्त दोनों चर के साथ दूर करने के बावजूद, उपरोक्त समाधान अभी भी यादृच्छिक बना हुआ है क्योंकि हम 0
प्रत्येक पुनरावृत्ति की ओर खुद को कम करने के लिए यादृच्छिक (एक यादृच्छिक रूप से चयनित सूचकांक पर शुरू) पर भरोसा कर रहे हैं ।
if (--random < 0) {
, जहाँ random
पहुँचता है -1
।
लौंग का घोल:
(defn pick-random [set] (let [sq (seq set)] (nth sq (rand-int (count sq)))))
nth
तत्व प्राप्त करने के लिए आपको आवश्यक seq
रूप से पार करना होगा ।
सी ++। यह उचित रूप से त्वरित होना चाहिए, क्योंकि इसे पूरे सेट पर पुनरावृत्ति की आवश्यकता नहीं है, या इसे छांटना चाहिए। यह अधिकांश आधुनिक संकलक के साथ बॉक्स से बाहर काम करना चाहिए, यह मानते हुए कि वे समर्थन करते हैं ट्र 1 का । यदि नहीं, तो आपको बूस्ट का उपयोग करने की आवश्यकता हो सकती है।
बूस्ट डॉक्स , यहाँ उपयोगी यह समझाने के लिए कर रहे हैं, भले ही आप बूस्ट का उपयोग नहीं करते।
चाल इस तथ्य का उपयोग करने के लिए है कि डेटा को बाल्टी में विभाजित किया गया है, और जल्दी से एक यादृच्छिक रूप से चुनी गई बाल्टी (उचित संभावना के साथ) की पहचान करने के लिए।
//#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;
}
}
उपरोक्त समाधान विलंबता के संदर्भ में बोलते हैं, लेकिन चयनित किए जाने वाले प्रत्येक सूचकांक की समान संभावना की गारंटी नहीं देते हैं।
अगर उस पर विचार करने की आवश्यकता है, तो जलाशय के नमूने की कोशिश करें। http://en.wikipedia.org/wiki/Reservoir_sampling ।
Collections.shuffle () (जैसा कि कुछ ने सुझाव दिया है) एक ऐसे एल्गोरिथ्म का उपयोग करता है।
चूंकि आपने कहा "अन्य भाषाओं के लिए समाधान भी स्वागत योग्य हैं", यहाँ पायथन के लिए संस्करण है:
>>> import random
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
4
क्या आप सेट / सरणी का आकार / लंबाई प्राप्त नहीं कर सकते, 0 और आकार / लंबाई के बीच एक यादृच्छिक संख्या उत्पन्न कर सकते हैं, फिर उस तत्व को कॉल करें जिसका सूचकांक उस संख्या से मेल खाता है? हैशसेट में एक .size () विधि है, मुझे पूरा यकीन है।
Psuedocode में -
function randFromSet(target){
var targetLength:uint = target.length()
var randomIndex:uint = random(0,targetLength);
return target[randomIndex];
}
PHP, "सेट" मानकर एक सरणी है:
$foo = array("alpha", "bravo", "charlie");
$index = array_rand($foo);
$val = $foo[$index];
Mersenne ट्विस्टर फ़ंक्शन बेहतर हैं, लेकिन PHP में array_rand के बराबर MT नहीं है।
आइकन में एक सेट प्रकार और एक यादृच्छिक-तत्व ऑपरेटर है, एकात्मक "?", इसलिए अभिव्यक्ति
? set( [1, 2, 3, 4, 5] )
1 और 5 के बीच एक यादृच्छिक संख्या का उत्पादन करेगा।
एक कार्यक्रम चलाने पर यादृच्छिक बीज को 0 से प्रारंभ किया जाता है, इसलिए प्रत्येक रन उपयोग पर अलग-अलग परिणाम उत्पन्न करने के लिए randomize()
C # में
Random random = new Random((int)DateTime.Now.Ticks);
OrderedDictionary od = new OrderedDictionary();
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"]);
जावास्क्रिप्ट समाधान;)
function choose (set) {
return set[Math.floor(Math.random() * set.length)];
}
var set = [1, 2, 3, 4], rand = choose (set);
या वैकल्पिक रूप से:
Array.prototype.choose = function () {
return this[Math.floor(Math.random() * this.length)];
};
[1, 2, 3, 4].choose();
गणित में:
a = {1, 2, 3, 4, 5}
a[[ ⌈ Length[a] Random[] ⌉ ]]
या, हाल के संस्करणों में, बस:
RandomChoice[a]
इसे एक डाउन-वोट प्राप्त हुआ, शायद इसलिए कि इसमें स्पष्टीकरण का अभाव है, इसलिए यहां एक है:
Random[]
0 और 1 के बीच एक छद्म आयामी फ्लोट उत्पन्न करता है। यह सूची की लंबाई से गुणा किया जाता है और फिर छत के फ़ंक्शन का उपयोग अगले पूर्णांक तक गोल करने के लिए किया जाता है। यह सूचकांक फिर से निकाला जाता हैa
।
चूंकि हैश टेबल की कार्यक्षमता अक्सर मैथेमेटिका में नियमों के साथ की जाती है, और नियमों को सूचियों में संग्रहीत किया जाता है, कोई भी उपयोग कर सकता है:
a = {"Badger" -> 5, "Bird" -> 1, "Fox" -> 3, "Frog" -> 2, "Wolf" -> 4};
कैसे बस के बारे में?
public static <A> A getRandomElement(Collection<A> c, Random r) {
return new ArrayList<A>(c).get(r.nextInt(c.size()));
}
मज़े के लिए मैंने अस्वीकृति के नमूने के आधार पर एक RandomHashSet लिखा। यह थोड़ा हैसी है, क्योंकि हैशपॉप हमें सीधे टेबल तक पहुंचने नहीं देता है, लेकिन यह ठीक काम करना चाहिए।
यह किसी भी अतिरिक्त मेमोरी का उपयोग नहीं करता है, और लुकअप समय O (1) amortized है। (क्योंकि जावा हैशटेबल सघन है)।
class RandomHashSet<V> extends AbstractSet<V> {
private Map<Object,V> map = new HashMap<>();
public boolean add(V v) {
return map.put(new WrapKey<V>(v),v) == null;
}
@Override
public Iterator<V> iterator() {
return new Iterator<V>() {
RandKey key = new RandKey();
@Override public boolean hasNext() {
return true;
}
@Override public V next() {
while (true) {
key.next();
V v = map.get(key);
if (v != null)
return v;
}
}
@Override public void remove() {
throw new NotImplementedException();
}
};
}
@Override
public int size() {
return map.size();
}
static class WrapKey<V> {
private V v;
WrapKey(V v) {
this.v = v;
}
@Override public int hashCode() {
return v.hashCode();
}
@Override public boolean equals(Object o) {
if (o instanceof RandKey)
return true;
return v.equals(o);
}
}
static class RandKey {
private Random rand = new Random();
int key = rand.nextInt();
public void next() {
key = rand.nextInt();
}
@Override public int hashCode() {
return key;
}
@Override public boolean equals(Object o) {
return true;
}
}
}
जावा 8 के साथ सबसे आसान है:
outbound.stream().skip(n % outbound.size()).findFirst().get()
n
एक यादृच्छिक पूर्णांक कहां है। बेशक यह उस से कम प्रदर्शन की हैfor(elem: Col)
अमरूद के साथ हम खोथ के जवाब से थोड़ा बेहतर कर सकते हैं:
public static E random(Set<E> set) {
int index = random.nextInt(set.size();
if (set instanceof ImmutableSet) {
// ImmutableSet.asList() is O(1), as is .get() on the returned list
return set.asList().get(index);
}
return Iterables.get(set, index);
}
यदि आप वास्तव Set
में यादृच्छिकता पर बिना किसी गारंटी के "किसी भी" ऑब्जेक्ट को चुनना चाहते हैं, तो सबसे आसान इट्रेटर द्वारा लौटाया गया पहला है।
Set<Integer> s = ...
Iterator<Integer> it = s.iterator();
if(it.hasNext()){
Integer i = it.next();
// i is a "random" object from set
}
शुरुआती बिंदु के रूप में खोथ के उत्तर का उपयोग करते हुए एक सामान्य समाधान।
/**
* @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++;
}
return null;
}
दुर्भाग्य से, यह मानक पुस्तकालय सेट कंटेनरों में से किसी में भी कुशलतापूर्वक (ओ (एन) से बेहतर) नहीं किया जा सकता है।
यह अजीब है, क्योंकि हैश सेट के साथ-साथ बाइनरी सेट में यादृच्छिक पिक फ़ंक्शन को जोड़ना बहुत आसान है। हैश सेट को विरल नहीं करने के लिए, आप यादृच्छिक प्रविष्टियों की कोशिश कर सकते हैं, जब तक कि आपको हिट न मिले। एक बाइनरी ट्री के लिए, आप अधिकतम O (log2) चरणों के साथ, बाएँ या दाएँ सबट्री के बीच बेतरतीब ढंग से चुन सकते हैं। मैंने नीचे के डेमो को लागू किया है:
import random
class Node:
def __init__(self, object):
self.object = object
self.value = hash(object)
self.size = 1
self.a = self.b = None
class RandomSet:
def __init__(self):
self.top = None
def add(self, object):
""" Add any hashable object to the set.
Notice: In this simple implementation you shouldn't add two
identical items. """
new = Node(object)
if not self.top: self.top = new
else: self._recursiveAdd(self.top, new)
def _recursiveAdd(self, top, new):
top.size += 1
if new.value < top.value:
if not top.a: top.a = new
else: self._recursiveAdd(top.a, new)
else:
if not top.b: top.b = new
else: 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]*10
for i in xrange(10000):
dists[s.pickRandom()] += 1
print dists
मुझे आउटपुट के रूप में [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] मिला, इसलिए वितरण अच्छा हुआ।
मैं अपने लिए एक ही समस्या से जूझ रहा हूं, और मैंने अभी तक यह तय नहीं किया है कि इस अधिक कुशल पिक का प्रदर्शन लाभ अजगर आधारित संग्रह का उपयोग करने के ओवरहेड के लायक है। मैं निश्चित रूप से इसे परिष्कृत कर सकता हूं और इसे सी में अनुवाद कर सकता हूं, लेकिन आज मेरे लिए बहुत काम है :)