답변:
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
and HashMap
: [element-> index]를 사용하는 Java 용 빠른 솔루션 .
동기 부여 : 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()
것은 반 안전합니다. 또한 OP는 동시성에 대해 아무 말도하지 않았으므로 유효하고 좋은 대답입니다.
dta
( Iterators.unmodifiableIterator
예 : 구아바를 통해 수행 할 수 있음 ). 그렇지 않으면 AbstractSet의 removeAll 및 retainAll 및 해당 반복자와 함께 작업하는 부모의 기본 구현은 사용자를 엉망으로 만듭니다 RandomSet
!
이것은 허용 된 답변의 for-each 루프보다 빠릅니다.
int index = rand.nextInt(set.size());
Iterator<Object> iter = set.iterator();
for (int i = 0; i < index; i++) {
iter.next();
}
return iter.next();
for-each 구문은 Iterator.hasNext()
모든 루프를 호출 하지만 이후 index < set.size()
로이 검사는 불필요한 오버 헤드입니다. 속도가 10-20 % 향상되었지만 YMMV가 나타났습니다. 또한 여분의 return 문을 추가하지 않고도 컴파일됩니다.
이 코드 (및 대부분의 다른 답변)는 Set뿐만 아니라 모든 Collection에 적용 할 수 있습니다. 일반적인 방법 양식에서 :
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();
}
}
Java로 작성하려면 요소를 일종의 임의 액세스 콜렉션 (예 : ArrayList)에 복사하는 것을 고려해야합니다. 집합이 작지 않으면 선택한 요소에 액세스하는 데 비용이 많이 듭니다 (O (1) 대신 O (n)). [ed : 목록 사본도 O (n)입니다]
또는 요구 사항과 더 일치하는 다른 Set 구현을 찾을 수 있습니다. Commons Collections 의 ListOrderedSet 은 유망한 것으로 보입니다.
자바에서 :
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);
이것은 답변 (Khoth)과 동일하지만 불필요 size
하고 i
변수가 제거되었습니다.
int random = new Random().nextInt(myhashSet.size());
for(Object obj : myhashSet) {
if (random-- == 0) {
return obj;
}
}
위에서 언급 한 두 가지 변수를 제거하더라도 위의 해결 방법은 무작위로 유지됩니다 (임의로 선택한 인덱스에서 시작하여) 0
.
if (--random < 0) {
, 어디에 random
도달 -1
.
C ++. 전체 세트를 반복하거나 정렬 할 필요가 없으므로 합리적으로 빠릅니다. tr1 을 지원한다고 가정하면 대부분의 최신 컴파일러에서 즉시 사용할 수 있습니다. 그렇지 않으면 Boost를 사용해야 할 수도 있습니다.
부스트 문서는 당신이 부스트를 사용하지 않는 경우에도, 이것을 설명하는 여기에 도움이됩니다.
비결은 데이터가 버킷으로 나뉘어져 있다는 사실을 활용하고 무작위로 선택한 버킷을 적절한 확률로 신속하게 식별하는 것입니다.
//#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 () (몇 안되는 사람)은 그러한 알고리즘 중 하나를 사용합니다.
"다른 언어에 대한 솔루션도 환영합니다"라고 말 했으므로 다음은 Python 버전입니다.
>>> import random
>>> random.choice([1,2,3,4,5,6])
3
>>> random.choice([1,2,3,4,5,6])
4
세트 / 배열의 크기 / 길이를 가져 와서 0과 크기 / 길이 사이의 난수를 생성 한 다음 색인이 해당 숫자와 일치하는 요소를 호출 할 수 없습니까? HashSet에는 .size () 메서드가 있습니다.
유사 코드에서-
function randFromSet(target){
var targetLength:uint = target.length()
var randomIndex:uint = random(0,targetLength);
return target[randomIndex];
}
"set"이 배열이라고 가정하면 PHP는 다음과 같습니다.
$foo = array("alpha", "bravo", "charlie");
$index = array_rand($foo);
$val = $foo[$index];
Mersenne Twister 함수는 더 좋지만 PHP에는 array_rand에 해당하는 MT가 없습니다.
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();
Mathematica에서 :
a = {1, 2, 3, 4, 5}
a[[ ⌈ Length[a] Random[] ⌉ ]]
또는 최신 버전에서 간단히 :
RandomChoice[a]
설명이 부족하기 때문에 다운 투표권을 받았으므로 여기에 하나가 있습니다.
Random[]
0과 1 사이의 의사 난수 부동 소수점을 생성합니다. 이것은 목록의 길이에 곱한 다음 천장 함수를 사용하여 다음 정수로 올림합니다. 이 색인은에서 추출됩니다 a
.
해시 테이블 기능은 Mathematica의 규칙으로 자주 수행되며 규칙은 목록에 저장되므로 다음을 사용할 수 있습니다.
a = {"Badger" -> 5, "Bird" -> 1, "Fox" -> 3, "Frog" -> 2, "Wolf" -> 4};
재미있게 나는 거부 샘플링을 기반으로 RandomHashSet을 작성했습니다. HashMap은 우리가 테이블에 직접 액세스 할 수 없기 때문에 약간 해키이지만 잘 작동합니다.
추가 메모리를 사용하지 않으며 조회 시간이 O (1)로 상각됩니다. (Java HashTable이 조밀하기 때문에).
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;
}
}
}
구아바 를 사용하면 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
}
Khoth의 답변을 출발점으로 사용하는 일반적인 솔루션.
/**
* @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 (n)보다 낫습니다).
이진 집합뿐만 아니라 해시 집합에 무작위 선택 기능을 추가하는 것이 매우 쉽기 때문에 이상합니다. 희소하지 않는 해시 세트에서는 적중을 얻을 때까지 임의의 항목을 시도 할 수 있습니다. 이진 트리의 경우 최대 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]을 얻었으므로 분포 솔기가 양호합니다.
나는 나 자신을 위해 같은 문제로 어려움을 겪었고, 아직이 더 효율적인 선택의 성능 향상이 파이썬 기반 컬렉션을 사용하는 오버 헤드 가치가 있다고 날씨를 결정하지 않았습니다. 물론 그것을 다듬고 C로 번역 할 수는 있었지만 오늘은 너무 많은 일입니다. :)