আমি কীভাবে একটি সেট থেকে একটি এলোমেলো উপাদান চয়ন করব? আমি জাভাতে হ্যাশসেট বা লিংকডহ্যাশসেট থেকে এলোমেলো উপাদান বাছতে আগ্রহী। অন্যান্য ভাষার সমাধানও স্বাগত।
আমি কীভাবে একটি সেট থেকে একটি এলোমেলো উপাদান চয়ন করব? আমি জাভাতে হ্যাশসেট বা লিংকডহ্যাশসেট থেকে এলোমেলো উপাদান বাছতে আগ্রহী। অন্যান্য ভাষার সমাধানও স্বাগত।
উত্তর:
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
এবং এর ব্যবহারের জন্য দ্রুত সমাধান 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
উদাহরণস্বরূপ এটি পেয়ারা দিয়ে অর্জন করা যেতে পারে )। অন্যথায় যেমন অ্যাবস্ট্রাক্টসেটে সমস্ত অপসারণ এবং রক্ষণাবেক্ষণের ডিফল্ট বাস্তবায়ন এবং সেই পুনরাবৃত্তকারীর সাথে কাজ করা এর পিতামাতারা আপনার গোলমাল করবে 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% বৃদ্ধি পেয়েছি, তবে ওয়াইএমএমভি। (এছাড়াও, অতিরিক্ত রিটার্নের বিবৃতি যোগ না করেই এই সংকলন করে))
মনে রাখবেন যে এই কোডটি (এবং অন্যান্য উত্তরগুলি) কেবল সেট নয়, যে কোনও সংকলনে প্রয়োগ করা যেতে পারে। জেনেরিক পদ্ধতিতে:
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();
}
}
আপনি যদি জাভাতে এটি করতে চান তবে আপনার উপাদানগুলিকে একরকম এলোমেলো-অ্যাক্সেস সংগ্রহে (যেমন একটি অ্যারেলিস্ট) অনুলিপি করা উচিত। কারণ, আপনার সেটটি ছোট না হলে নির্বাচিত উপাদান অ্যাক্সেস করা ও (1) এর পরিবর্তে (O (n) ব্যয়বহুল হবে 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
।
সি ++। এটি যুক্তিসঙ্গতভাবে দ্রুত হওয়া উচিত কারণ এটি পুরো সেটটিতে পুনরাবৃত্তি বা বাছাইয়ের প্রয়োজন হয় না require এটি টিআর 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.wikedia.org/wiki/Reservoir_sampling ।
কালেকশন.শ্যাফল () কয়েকটি দ্বারা প্রস্তাবিত হিসাবে) এরকম একটি অ্যালগরিদম ব্যবহার করা হয়।
আপনি যেহেতু "অন্যান্য ভাষার সমাধানও স্বাগত" বলেছিলেন তাই পাইথনের সংস্করণটি এখানে:
>>> import random
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
4
আপনি কি কেবল সেট / অ্যারের আকার / দৈর্ঘ্য পেতে পারেন না, 0 এবং আকার / দৈর্ঘ্যের মধ্যে একটি এলোমেলো সংখ্যা তৈরি করতে পারেন, তারপরে যে সংখ্যার সূচকটি মেলে সেই উপাদানটিকে কল করতে পারেন? হ্যাশসেটের একটি। সাইজ () পদ্ধতি রয়েছে, আমি নিশ্চিত।
সাইয়েডোকোডে -
function randFromSet(target){
var targetLength:uint = target.length()
var randomIndex:uint = random(0,targetLength);
return target[randomIndex];
}
পিএইচপি, ধরে নিচ্ছে "সেট" একটি অ্যারে:
$foo = array("alpha", "bravo", "charlie");
$index = array_rand($foo);
$val = $foo[$index];
মার্সেন টুইস্টার ফাংশনগুলি আরও ভাল তবে পিএইচপি-তে অ্যারে_রেন্ডের সমান এমটি নেই।
আইকনটিতে একটি সেট টাইপ এবং একটি এলোমেলো-উপাদান উপাদান অপারেটর, unary "?" রয়েছে, তাই অভিব্যক্তি
? set( [1, 2, 3, 4, 5] )
1 এবং 5 এর মধ্যে একটি এলোমেলো সংখ্যা তৈরি করবে।
কোনও প্রোগ্রাম চালানো হলে এলোমেলো বীজ 0 টি শুরু করা হয়, যাতে প্রতিটি রান ব্যবহারের ক্ষেত্রে বিভিন্ন ফলাফল পাওয়া যায় randomize()
সি # তে
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()));
}
মজাদার জন্য আমি প্রত্যাখ্যানের নমুনার উপর ভিত্তি করে একটি র্যান্ডমহ্যাশসেট লিখেছিলাম। এটি কিছুটা হ্যাকি, যেহেতু হ্যাশম্যাপ আমাদের সরাসরি এটির টেবিলটি অ্যাক্সেস করতে দেয় না, তবে এটি ঠিক কাজ করা উচিত।
এটি কোনও অতিরিক্ত মেমরি ব্যবহার করে না এবং দেখার সময় হে (1) মোড়িত। (কারণ জাভা হ্যাশ টেবিলটি ঘন)।
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)
সঙ্গে পেয়ারা আমরা Khoth এর উত্তর বেশী ভালো একটু করতে পারেন:
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;
}
দুর্ভাগ্যক্রমে, কোনও স্ট্যান্ডার্ড লাইব্রেরি সেট ধারকগুলির মধ্যে দক্ষতার সাথে এটি (ও (এন) এর চেয়ে ভাল) করা যায় না।
এটি অদ্ভুত, যেহেতু হ্যাশ সেটগুলির পাশাপাশি বাইনারি সেটগুলিতে এলোমেলোভাবে পিক ফাংশন যুক্ত করা খুব সহজ। একটি হ্যাশ সেট ছড়িয়ে না দেওয়ার জন্য, আপনি হিট না হওয়া পর্যন্ত আপনি এলোমেলো এন্ট্রি চেষ্টা করতে পারেন। বাইনারি গাছের জন্য, আপনি সর্বোচ্চ ও (লগ 2) পদক্ষেপের সাহায্যে বাম বা ডান সাবট্রির মধ্যে এলোমেলোভাবে চয়ন করতে পারেন। আমি নীচের পরে একটি ডেমো প্রয়োগ করেছি:
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] পেয়েছি তাই বিতরণটি ভাল হয়।
আমি নিজের জন্য একই সমস্যাটির সাথে লড়াই করেছি এবং আমি এখনও আবহাওয়া স্থির করে নি নি যে আরও কার্যকর পিকের পারফরম্যান্স লাভটি পাইথন ভিত্তিক সংগ্রহ ব্যবহারের ওভারহেডের পক্ষে মূল্যবান। আমি অবশ্যই এটি পরিমার্জন করতে এবং এটি সি তে অনুবাদ করতে পারি, তবে এটি আমার পক্ষে আজ খুব বেশি কাজ :)