Istnieje prosty algorytm do losowego wybierania przedmiotu, w którym przedmioty mają indywidualną wagę:
1) obliczyć sumę wszystkich wag
2) wybierz liczbę losową równą 0 lub większą i mniejszą niż suma wag
3) przeglądaj elementy pojedynczo, odejmując ich wagę od liczby losowej, aż otrzymasz przedmiot, w którym liczba losowa jest mniejsza niż waga tego przedmiotu
Pseudokod ilustrujący to:
int sum_of_weight = 0;
for(int i=0; i<num_choices; i++) {
sum_of_weight += choice_weight[i];
}
int rnd = random(sum_of_weight);
for(int i=0; i<num_choices; i++) {
if(rnd < choice_weight[i])
return i;
rnd -= choice_weight[i];
}
assert(!"should never get here");
Powinno to być proste, aby dostosować się do twoich pojemników do przyspieszania i tym podobnych.
Jeśli twoje ciężary są rzadko zmieniane, ale często wybierasz jeden losowo i tak długo, jak twój pojemnik przechowuje wskaźniki do obiektów lub ma więcej niż kilkadziesiąt przedmiotów (w zasadzie musisz profilować, aby wiedzieć, czy to pomaga, czy przeszkadza) , to jest optymalizacja:
Przechowując skumulowaną sumę wag w każdej pozycji, możesz skorzystać z wyszukiwania binarnego w celu wybrania pozycji odpowiadającej masie pobrania.
Jeśli nie znasz liczby pozycji na liście, istnieje bardzo zgrabny algorytm zwany próbkowaniem zbiorników, który można dostosować do ważenia.
A Monte Carlo method called Russian roulette is used to choose one of these actions
pojawia się w zasobnikach podczas wyszukiwania go w Google. „algorytm rosyjskiej ruletki”. Można by argumentować, że wszyscy ci ludzie mają złe imię.Zaktualizowana odpowiedź na stare pytanie. Możesz to łatwo zrobić w C ++ 11 za pomocą tylko std :: lib:
Wyjście w moim systemie:
Zauważ, że większość powyższego kodu poświęcona jest tylko wyświetlaniu i analizowaniu danych wyjściowych. Faktyczna generacja to tylko kilka wierszy kodu. Dane wyjściowe pokazują, że żądane „prawdopodobieństwa” zostały uzyskane. Musisz podzielić żądane dane wyjściowe przez 1,5, ponieważ do tego sumują się żądania.
źródło
std::discrete_distribution
zamiast tegostd::piecewise_constant_distribution
byłoby jeszcze lepiej.Jeśli twoje wagi zmieniają się wolniej niż są rysowane, C ++ 11
discrete_distribution
będzie najłatwiejszy:Należy jednak pamiętać, że c ++ 11
discrete_distribution
oblicza wszystkie skumulowane sumy podczas inicjalizacji. Zwykle jest to pożądane, ponieważ przyspiesza czas próbkowania przy jednorazowym koszcie O (N). Ale w przypadku szybko zmieniającej się dystrybucji będzie to wiązało się z dużym kosztem obliczeń (i pamięci). Na przykład, jeśli wagi reprezentowały liczbę elementów i za każdym razem, gdy rysujesz jeden, usuwasz go, prawdopodobnie będziesz potrzebować niestandardowego algorytmu.Odpowiedź Willa https://stackoverflow.com/a/1761646/837451 pozwala uniknąć tego narzutu, ale będzie wolniejsza w użyciu niż z C ++ 11, ponieważ nie może używać wyszukiwania binarnego.
Aby zobaczyć, że to robi, możesz zobaczyć odpowiednie linie (
/usr/include/c++/5/bits/random.tcc
na mojej instalacji Ubuntu 16.04 + GCC 5.3):źródło
To, co robię, gdy muszę zważyć liczby, używa losowej liczby jako wagi.
Na przykład: Potrzebuję generowania liczb losowych od 1 do 3 o następujących wagach:
Następnie używam:
W tym przypadku losowo ma 10% prawdopodobieństw 1, 30% 2 i 60% 3.
Możesz się nim bawić zgodnie ze swoimi potrzebami.
Mam nadzieję, że mogę ci pomóc, powodzenia!
źródło
Zbuduj worek (lub std :: wektor) wszystkich przedmiotów, które można wybrać.
Upewnij się, że liczba każdego elementu jest proporcjonalna do Twojej wagi.
Przykład:
Więc miej worek ze 100 pozycjami z 60 1, 35 2 i 5 3.
Teraz losowo posortuj torbę (std :: random_shuffle)
Wybierz elementy z worka po kolei, aż będzie pusty.
Po opróżnieniu zmień losowo worek i zacznij od nowa.
źródło
1,2,2
produkującą 1 1/3 czasu i 2 2/3. Losuj tablicę, wybierz pierwszą, powiedzmy 2, teraz następny wybrany element jest zgodny z rozkładem 1 1/2 czasu i 2 1/2 czasu. Rozumieć?Wybierz losową liczbę na [0,1), która powinna być domyślnym operatorem () dla RNG doładowania. Wybierz pozycję z funkcją skumulowanej gęstości prawdopodobieństwa> = ta liczba:
Gdzie random01 () zwraca double> = 0 i <1. Zauważ, że powyższe nie wymaga sumowania prawdopodobieństw do 1; normalizuje je dla ciebie.
p jest po prostu funkcją przypisującą prawdopodobieństwo do elementu w kolekcji [początek, koniec). Możesz go pominąć (lub użyć tożsamości), jeśli masz tylko sekwencję prawdopodobieństw.
źródło
Zaimplementowałem kilka prostych ważonych algorytmów losowych .
źródło