programing

세트에서 랜덤 요소 선택

copysource 2022. 7. 16. 13:37
반응형

세트에서 랜덤 요소 선택

세트에서 랜덤 요소를 선택하려면 어떻게 해야 합니까?특히 Java의 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++;
}

어느 정도 관련성이 있는 것을 알고 계십니까?

에는 컬렉션 전체를 shuffing 하는 편리한 방법이 있습니다.

ArrayList a. a. 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();
    }
}

이는 수용된 응답의 각 루프에 대한 속도보다 빠릅니다.

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입니다.(또한 추가 수익률 문구를 추가하지 않아도 컴파일 할 수 있습니다.)

이 코드(및 기타 대부분의 응답)는 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 8의 경우:

static <E> E getRandomSetElement(Set<E> set) {
    return set.stream().skip(new Random().nextInt(set.size())).findFirst().orElse(null);
}

Java에서 실행하는 경우 요소를 일종의 랜덤액세스 컬렉션(ArrayList 등)에 복사하는 것을 검토해 주십시오.세트가 작지 않은 한 선택한 요소에 액세스하는 데 비용이 많이 들기 때문입니다(O(1) 대신 O(n)).[ed: 목록 복사도 O(n)]

또는 요구 사항에 더 가까운 다른 Set 구현을 찾을 수도 있습니다.Commons Collections의 List Ordered Set은 유망해 보입니다.

자바어:

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)과하지만, 한 답안(Khoth)이 .size ★★★★★★★★★★★★★★★★★」i변수가 삭제되었습니다.

    int random = new Random().nextInt(myhashSet.size());
    for(Object obj : myhashSet) {
        if (random-- == 0) {
            return obj;
        }
    }

, 인 채로 에해, 그 입니다. 왜냐하면 우리는 랜덤(임의로 선택된 지수에서 시작)에 의존하여 자신을 감소시키기 때문입니다.0각 반복에 걸쳐서.

클로저 솔루션:

(defn pick-random [set] (let [sq (seq set)] (nth sq (rand-int (count sq)))))

Perl 5

@hash_keys = (keys %hash);
$rand = int(rand(@hash_keys));
print $hash{$hash_keys[$rand]};

여기 그것을 하는 한 가지 방법이 있다.

C++. 이 작업은 전체 세트를 반복하거나 정렬할 필요가 없기 때문에 상당히 빠릅니다.대부분의 최신 컴파일러가 tr1을 지원한다고 가정하면 이 기능은 개봉 즉시 사용할 수 있습니다.그렇지 않으면 Boost를 사용해야 할 수 있습니다.

Boost 문서를 사용하면 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 를 참조해 주세요.
의해 shuffle은 이러한 알고리즘 중 합니다.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() 메서드가 있습니다.

psuedocode의 경우 -

function randFromSet(target){
 var targetLength:uint = target.length()
 var randomIndex:uint = random(0,targetLength);
 return target[randomIndex];
}

PHP('set'이 어레이라고 가정):

$foo = array("alpha", "bravo", "charlie");
$index = array_rand($foo);
$val = $foo[$index];

Mersenne Twister 함수는 더 좋지만 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"]);

Javascript 솔루션;)

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();

lisp로

(defun pick-random (set)
       (nth (random (length set)) set))

매스매티카:

a = {1, 2, 3, 4, 5}

a[[ ⌈ Length[a] Random[] ⌉ ]]

또는 최근 버전에서는 다음과 같이 간단하게 할 수 있습니다.

RandomChoice[a]

이것은 설명이 부족하기 때문에 낮은 평가를 받고 있습니다.그러면, 다음과 같은 것이 있습니다.

Random[]는 0 ~ 1. ~1 의 합니다.이 값에 리스트 길이를 곱한 다음 상한 함수를 사용하여 다음 정수로 반올림합니다. 이 를 뺄 수 있다에서 합니다.a.

해시 테이블 기능은 Mathematica 규칙에서 자주 수행되며 규칙은 목록에 저장되므로 다음을 사용할 수 있습니다.

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()));
}

재미삼아 거부 샘플링을 바탕으로 랜덤 해시셋을 작성했습니다.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;
        }
    }
}

Java 8에서 가장 쉬운 것은 다음과 같습니다.

outbound.stream().skip(n % outbound.size()).findFirst().get()

어디에n는 랜덤 정수입니다.물론 퍼포먼스는 퍼포먼스보다 떨어집니다.for(elem: Col)

Guava를 사용하면 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);
}

Java 8+ 스트림:

    static <E> Optional<E> getRandomElement(Collection<E> collection) {
        return collection
                .stream()
                .skip(ThreadLocalRandom.current()
                .nextInt(collection.size()))
                .findAny();
    }

Joshua Bone답변에 기초하지만 약간의 변화가 있습니다.

  • 병렬 작업에서 약간의 성능 향상을 위해 Streams 요소의 순서를 무시합니다.
  • 현재 스레드의 ThreadLocalRandom을 사용합니다.
  • 모든 컬렉션 유형을 입력으로 받아들입니다.
  • null 대신 제공된 옵션을 반환합니다.

PHP, MT 사용:

$items_array = array("alpha", "bravo", "charlie");
$last_pos = count($items_array) - 1;
$random_pos = mt_rand(0, $last_pos);
$random_item = $items_array[$random_pos];

어레이를 사용하여 어레이로 세트를 전송할 수도 있습니다.아마도 소규모로 동작할 것입니다.최다 투표한 답변의 for loop은 O(n)입니다.

Object[] arr = set.toArray();

int v = (int) arr[rnd.nextInt(arr.length)];

정말로 '임의의' 오브젝트를 선택하고 싶은 경우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;
}

언급URL : https://stackoverflow.com/questions/124671/picking-a-random-element-from-a-set

반응형