Chcę wdrożyć system oparty na szansie, który jest uprzedzony przez wcześniejsze zdarzenie.
Tło: Kilka lat temu pamiętam aktualizację World of Warcraft, która ogłosiła, że wprowadzili nowy kalkulator szans, który przeciwdziałałby kolczastym łańcuchom wydarzeń. (np. zadawanie krytycznych ciosów lub unikanie kilku razy z rzędu). Pomysł polegał na tym, że w przypadku unikania trafienia szansa na uniknięcie kolejnego trafienia byłaby zmniejszona, ale działałoby to w obie strony. Nie uchylanie się od trafienia zwiększyłoby szanse na uniknięcie kolejnego trafienia. Główna sztuczka polegała na tym, że podczas kilku prób szansa na unik nadal odpowiadałaby procentowi podanemu graczowi w jego arkuszu statystyk.
Tego rodzaju system bardzo mnie wtedy intrygował, a teraz jestem w potrzebie takiego rozwiązania.
Oto moje kłopoty:
- Zgaduję, że byłbym w stanie znaleźć zasoby online na temat wdrażania takiego systemu, ale być może brakuje mi odpowiednich słów, aby go znaleźć.
- Potrzebuję również tego podejścia, aby dopasować system, który nie jest dwumianowy (tj. Dwa wyniki), ale zamiast tego zawiera 4 wzajemnie wykluczające się zdarzenia.
Moje obecne podejście jest podobne do systemu biletów losowych. Kiedy zdarzenie ma miejsce, zmieniam wagi na korzyść wszystkich innych zdarzeń. Mogłoby to zadziałać, gdyby cztery zdarzenia miały być jednakowo prawdopodobne, ale w moim przypadku musi być znacznie bardziej powszechne. Ale ponieważ zdarzenie dominujące zdarza się częściej, przesuwa on ciężary innych o wiele wyżej niż zamierzano i wydaje mi się, że nie mogę znaleźć liczb dla przesunięć wagi, które są potrzebne, aby utrzymać średnią liczbę biletów wokół początkowych wartości, które były zdarzeniem dany.
Doceniono by kilka wskazówek kierunkowych lub wyraźny przykład.
Odpowiedzi:
Zasadniczo pytasz o „pół-losowy” generator zdarzeń, który generuje zdarzenia o następujących właściwościach:
Średnie tempo wystąpienia każdego zdarzenia jest określone z góry.
To samo zdarzenie jest mniej prawdopodobne, że wystąpi dwa razy z rzędu, niż byłoby przypadkowe.
Wydarzenia nie są w pełni przewidywalne.
Jednym ze sposobów na to jest najpierw zaimplementowanie generatora nieprzypadkowych zdarzeń, który spełnia cele 1 i 2, a następnie dodanie losowości, aby osiągnąć cel 3.
W przypadku generatora zdarzeń nieprzypadkowych możemy zastosować prosty algorytm ditheringu . W szczególności niech p 1 , p 2 , ..., p n będą względnymi prawdopodobieństwami zdarzeń 1 do n , a niech s = p 1 + p 2 + ... + p n będzie sumą wag. Następnie możemy wygenerować nieprzypadkową, maksymalnie równorzędną sekwencję zdarzeń, stosując następujący algorytm:
Początkowo niech e 1 = e 2 = ... = e n = 0.
Aby wygenerować zdarzenie, zwiększ każde e i o p i wyślij zdarzenie k, dla którego e k jest największe (zerwanie powiązań w dowolny sposób).
Decrement e k o s , a następnie powtórz czynności od punktu 2.
Na przykład, biorąc pod uwagę trzy zdarzenia A, B i C, przy p A = 5, p B = 4 i p C = 1, ten algorytm generuje coś w rodzaju następującej sekwencji wyników:
Zauważ, że ta sekwencja 30 zdarzeń zawiera dokładnie 15 As, 12 Bs i 3 Cs. Nie do końca optymalnie się rozprowadza - jest kilka przypadków dwóch jak z rzędu, których można było uniknąć - ale się zbliża.
Teraz, aby dodać losowość do tej sekwencji, masz kilka (niekoniecznie wykluczających się) opcji:
Możesz śledzić porady Philippa i utrzymać „talię” z N nadchodzących wydarzeniach, na pewnym odpowiednim rozmiarze numerem N . Za każdym razem, gdy musisz wygenerować zdarzenie, wybierasz losowe zdarzenie z talii, a następnie zamieniasz je na wyjście następnego zdarzenia za pomocą algorytmu ditheringu powyżej.
Zastosowanie tego do powyższego przykładu, przy N = 3, daje np .:
podczas gdy N = 10 daje bardziej losowy wygląd:
Zwróć uwagę, że wspólne zdarzenia A i B kończą się znacznie większą liczbą przebiegów z powodu przetasowania, podczas gdy rzadkie zdarzenia C są wciąż dość dobrze rozłożone.
Możesz wprowadzić losowość bezpośrednio do algorytmu ditheringu. Na przykład, zamiast zwiększając e I o p ı w etapie 2, można ją zwiększyć p ı x losowej (0, 2), w której losowy ( , b ) jest równomiernie rozproszony liczby losowej o wartości pomiędzy i b ; dałoby to wynik podobny do następującego:
lub możesz zwiększyć e i o p i + losowo (- c , c ), co dałoby (dla c = 0,1 × s ):
lub, dla c = 0,5 × s :
Zauważ, że schemat addytywny ma znacznie silniejszy efekt losowy dla rzadkich zdarzeń C niż dla wspólnych zdarzeń A i B, w porównaniu do multiplikatywnego; to może, ale nie musi być pożądane. Oczywiście, można również korzystać z niektórych kombinacji tych systemów, lub jakiegokolwiek innego dostosowywania do przyrostów, jak długo zachowuje właściwość, że średni przyrost e ja równa P I .
Alternatywnie można zakłócać wyjście algorytmu ditheringu, czasami zastępując wybrane zdarzenie k losowym (wybranym zgodnie z surowymi wagami p i ). Tak długo, jak użyjesz tego samego k w kroku 3, jak wypisujesz w kroku 2, proces ditheringu będzie miał tendencję do wyrównywania przypadkowych fluktuacji.
Na przykład, oto przykładowy wynik, z 10% szansą na losowe wybranie każdego zdarzenia:
a oto przykład z 50% szansą, że każde wyjście będzie losowe:
Można również rozważyć karmienie mieszankę czysto losowych i wygładzony wydarzeń na pokładzie / mieszania basenie, jak opisano powyżej, lub może losowanie algorytm ditheringu wybierając k losowo, jako ważone przez e i s (leczenia negatywnych ciężarów jako zero).
Ps. Oto kilka całkowicie losowych sekwencji zdarzeń, z tymi samymi średnimi wskaźnikami, dla porównania:
Styczna: Ponieważ w komentarzach pojawiła się debata na temat tego, czy w przypadku rozwiązań pokładowych konieczne jest umożliwienie opróżnienia pokładu przed ponownym napełnieniem, postanowiłem dokonać graficznego porównania kilku strategii napełniania pokładu:
Wykres kilku strategii generowania pół losowych rzutów monetą (przy średnim stosunku główek do reszek wynoszącym 50:50). Oś pozioma to liczba przewrotów, oś pionowa to skumulowana odległość od oczekiwanego stosunku, mierzona jako (głowice - ogony) / 2 = głowice - przewroty / 2.
Czerwone i zielone linie na wykresie pokazują dwa niepoparte na pokładzie algorytmy do porównania:
Pozostałe trzy linie (niebieski, fioletowy i cyjan) pokazują wyniki trzech strategii opartych na talii, z których każda została wdrożona przy użyciu talii 40 kart, która początkowo jest wypełniona 20 kartami „głów” i 20 kartami „ogonów”:
Oczywiście powyższy wykres jest tylko pojedynczą realizacją losowego procesu, ale jest dość reprezentatywny. W szczególności widać, że wszystkie procesy pokładowe mają ograniczone odchylenie i pozostają dość blisko czerwonej (deterministycznej) linii, podczas gdy czysto losowa zielona linia ostatecznie odchodzi.
(W rzeczywistości odchylenie linii niebieskiej, purpurowej i niebieskiej od zera jest ściśle ograniczone rozmiarem talii: niebieska linia nigdy nie może dryfować więcej niż 10 kroków od zera, fioletowa linia może uzyskać tylko 15 kroków od zera , a linia cyjanowa może dryfować co najwyżej o 20 kroków od zera. Oczywiście w praktyce żadna z linii, która faktycznie osiąga swój limit, jest bardzo mało prawdopodobna, ponieważ istnieje silna tendencja do powrotu bliżej zera, jeśli wędrują zbyt daleko poza.)
Na pierwszy rzut oka nie ma oczywistej różnicy między różnymi strategiami opartymi na talii (chociaż średnio niebieska linia pozostaje nieco bliżej czerwonej linii, a linia cyjanowa pozostaje nieco dalej), ale dokładniejsze sprawdzenie niebieskiej linii ujawnia wyraźny deterministyczny wzór: co 40 rysunków (oznaczonych przerywanymi szarymi pionowymi liniami) niebieska linia dokładnie spotyka czerwoną linię na zero. Linie fioletowe i cyjanowe nie są tak ściśle ograniczone i mogą trzymać się zera w dowolnym momencie.
We wszystkich strategiach opartych na talii ważną cechą, która ogranicza ich warianty, jest fakt, że podczas losowania kart z talii, determinacja jest uzupełniana . Gdyby karty użyte do uzupełnienia talii były wybierane losowo, wszystkie strategie oparte na talii byłyby nie do odróżnienia od czystego losowego wyboru (zielona linia).
źródło
Nie rzucaj kośćmi, rozdawaj karty.
Weź wszystkie możliwe wyniki RNG, umieść je na liście, losowo potasuj i zwróć wyniki w losowej kolejności. Gdy znajdziesz się na końcu listy, powtórz.
Wyniki będą nadal równomiernie rozmieszczone, ale poszczególne wyniki nie będą się powtarzać, chyba że ostatnia z listy będzie również pierwszą z następnych.
Jeśli jest to zbyt przewidywalne dla twojego gustu, możesz użyć listy, która jest
n
razy liczba możliwych wyników i umieścić każdy możliwy wynikn
razy przed tasowaniem. Lub możesz przetasować listę, zanim zostanie ona całkowicie iterowana.źródło
Możesz spróbować losowego wykresu Markowa . Rozważ każde zdarzenie, które może wystąpić, jako węzeł na wykresie. Z każdego wydarzenia utwórz link do siebie, które może nastąpić po nim. Każde z tych łączy jest ważone przez coś zwanego prawdopodobieństwem przejścia . Następnie wykonujesz losowy spacer po wykresie zgodnie z modelem przejścia.
Na przykład możesz mieć wykres przedstawiający wynik ataku (trafienie krytyczne, unik itp.). Zainicjuj węzeł początkowy na jeden wybrany losowo, biorąc pod uwagę statystyki gracza (po prostu „rzuć kostką”). Następnie, przy następnym ataku, zdecyduj, co stanie się dalej, biorąc pod uwagę model przejściowy.
Należy zadbać o to, aby zdecydować, jak ważić przejścia. Po pierwsze, wszystkie przejścia wychodzące z węzła muszą się sumować z prawdopodobieństwem 1. Jedną prostą rzeczą, którą możesz zrobić, jest przejście z każdego węzła do każdego innego węzła, z wagami równoważnymi prawdopodobieństwu wystąpienia tych zdarzeń a priori , biorąc pod uwagę, że bieżące zdarzenie nie może się powtórzyć.
Na przykład, jeśli masz trzy zdarzenia:
Możesz skonfigurować model przejścia tak, aby trafienie krytyczne nie powtórzyło się po prostu poprzez równomierne rozłożenie jego masy prawdopodobieństwa na inne zdarzenia:
EDYCJA: Jak mówią komentarze poniżej, ten model nie jest wystarczająco skomplikowany, aby uzyskać pożądane zachowanie. Zamiast tego może być konieczne dodanie wielu dodatkowych stanów!
źródło
Oto implementacja, którą utworzyłem w C #, która:
Dodałem kilka komentarzy, abyś mógł zobaczyć, co robię.
Mam nadzieję, że to pomoże, proszę sugerować ulepszenia tego kodu w komentarzach, dzięki!
źródło
Pozwól mi trochę uogólnić odpowiedź mklingen . Zasadniczo chcesz wdrożyć błąd Hazardzisty , ale przedstawię tutaj bardziej ogólną metodę:
Powiedz, że są
n
możliwe zdarzenia z prawdopodobieństwemp_1, p_2, ..., p_n
. Kiedy zdarzenie sięi
wydarzy, jego prawdopodobieństwo przeskaluje się ze współczynnikiem0≤a_i≤1/p_i
(to drugie jest ważne, w przeciwnym razie prawdopodobieństwo będzie większe niż jedno, a pozostałe zdarzenia muszą mieć ujemne prawdopodobieństwa , co w zasadzie oznacza „ anty ” zdarzenia. Lub coś takiego) typowoa_i<1
. Możesz na przykład wybraća_i=p_i
, co oznacza, że prawdopodobieństwo wystąpienia zdarzenia po raz drugi jest pierwotnym prawdopodobieństwem, że wydarzenie nastąpi dokładnie dwa razy z rzędu, np. Drugie rzut monetą miałby prawdopodobieństwo 1/4 zamiast 1/2. Z drugiej strony możesz też mieć cośa_i>1
, co oznaczałoby wywołanie „udaru szczęścia / nieszczęścia”.Wszystkie pozostałe zdarzenia powinny być jednakowo prawdopodobne względem siebie, tzn. Wszystkie muszą zostać przeskalowane o ten sam współczynnik,
b_i
tak aby suma wszystkich prawdopodobieństw była równa jeden, tj.Do tej pory takie proste. Ale teraz dodajmy kolejny wymóg: biorąc pod uwagę wszystkie możliwe sekwencje dwóch zdarzeń, wyodrębnione z nich prawdopodobieństwa pojedynczego zdarzenia będą pierwotnymi prawdopodobieństwami.
Pozwolić
oznacz prawdopodobieństwo wystąpienia zdarzenia
j
po wydarzeniui
i zwróć uwagę, żep_ij≠p_ji
chybab_i=b_j (2)
(co(1)
implikujea_j = 1 - a_i*p_i + (1-a_i)*p_i/p_j
). Tego właśnie wymaga twierdzenie Bayesa, co również implikujetak jak chcesz. Zwróć uwagę, jak to oznacza, że
a_i
naprawia się wszystkie pozostałe.Zobaczmy teraz, co się stanie, gdy zastosujemy tę procedurę wiele razy, tj. Dla sekwencji trzech i więcej zdarzeń. Istnieją zasadniczo dwie opcje wyboru ustalonych prawdopodobieństw trzeciego zdarzenia:
a) Zapomnij o pierwszym zdarzeniu i urządzeniu tak, jakby miało miejsce tylko drugie, tj
Pamiętaj, że zwykle narusza to Bayesa, ponieważ np.
p_jik≠p_ikj
W większości przypadków.b) Użyj prawdopodobieństw
p_ij
(dla ustalonychi
) jako nowych prawdopodobieństw,pi_j
z których uzyskasz nowe prawdopodobieństwa,pi_jk
aby zdarzeniek
miało miejsce w następnej kolejności. To, czy zmodyfikujesz,ai_j
czy nie, zależy od ciebie, ale pamiętaj, że nowebi_j
są zdecydowanie różne ze względu na zmodyfikowanepi_j
. Z drugiej strony wybórai_j
prawdopodobnie jest ograniczony przez wymaganie wszystkich permutacjiijk
wystąpienia z tym samym prawdopodobieństwem. Zobaczmy...i ich cykliczne permutacje, które muszą być równe dla odpowiednich przypadków.
Obawiam się, że moja kontynuacja będzie musiała chwilę poczekać ...
źródło
Myślę, że najlepszą opcją jest użycie losowego wyboru przedmiotów. Jest to implementacja dla C # tutaj , ale można je łatwo znaleźć lub wykonane w innych językach.
Pomysł polegałby na zmniejszeniu wagi opcji za każdym razem, gdy jest ona wybierana, i zwiększaniu jej za każdym razem, gdy nie jest wybierana.
Na przykład, jeśli zmniejszysz wagę wybranej opcji o
NumOptions-1
i zwiększysz wagę każdej innej opcji o 1 (uważając, aby usunąć przedmioty o wadze <0 i przeczytać je, gdy wzrosną powyżej 0) , każda opcja zostanie wybrana w przybliżeniu tyle samo razy w ciągu długiego okresu, ale ostatnio wybrane opcje będą znacznie mniej prawdopodobne.Problem z korzystaniem z losowego zamawiania, jak sugeruje wiele innych odpowiedzi, polega na tym, że po wybraniu każdej opcji, z wyjątkiem jednej, można z całkowitą pewnością przewidzieć, która opcja zostanie wybrana w następnej kolejności. To niezbyt losowe.
źródło
Moja odpowiedź jest nieprawidłowa, mój test był wadliwy.
Zostawiam tę odpowiedź tutaj do dyskusji i komentarzy, które wskazują na wady tego projektu, ale rzeczywisty test był niepoprawny.
źródło
Możesz zrobić to, co zasadniczo jest filtrem. Śledź n ostatnich wydarzeń. Prawdopodobieństwo to niektóre filtry zastosowane do tych zdarzeń. 0 filtr jest podstawowym prawdopodobieństwem, jeśli 0, to unikniesz, jeśli 1 nie. Załóżmy, że podstawa wynosiła 25%, a filtr zmniejsza się o połowę z każdą iteracją. Twój filtr to:
Jeśli chcesz, możesz kontynuować. Ogólne prawdopodobieństwo tego schematu jest nieco wyższe niż podstawowe prawdopodobieństwo 0,25. W rzeczywistości prawdopodobieństwo, biorąc pod uwagę ten sam schemat, jest (nazywam x rzeczywiste prawdopodobieństwo, p jest wejściem prawdopodobieństwa):
Rozwiązywanie dla x, znajdujemy odpowiedź brzmi
p(1+1/2+1/4+1/8)/(1+p(1/2+1/4+1/8)
, czy za naszym konkretnym przypadkux=0.38461538461
. Ale tak naprawdę chcesz znaleźć p, biorąc pod uwagę x. To okazuje się trudniejszym problemem. Jeśli założono filtr nieskończony, problemem staje sięx+x*p=2*p
lubp=x/(2-x)
. Więc zwiększając swój filtr, możesz następnie rozwiązać dla liczby p, która średnio da ci te same wyniki, ale w tempie zależnym od tego, ile sukcesów ostatnio się wydarzyło.Zasadniczo za pomocą poprzednich wartości określa się próg akceptacji w tej rundzie i przyjmuje wartość losową. Następnie wygeneruj następną losową wartość dla danego filtra.
źródło
Tak jak sam się zaproponowałeś, jednym z podejść do tego jest wdrożenie ważonej losowej. Chodzi o to, aby stworzyć generator liczb losowych (lub wyników), w którym wagi i wyniki mogą być modyfikowane.
Oto implementacja tego w Javie.
EDYCJA W przypadku, gdy chcesz automatycznie dostosowywać wagi, na przykład zwiększ szansę na A, gdy wynik to B. Możesz również:
nextOutcome()
metody, aby modyfikowała wagę zgodnie z wynikiemsetWeight()
do modyfikowania masy zgodnie z wynikiem.źródło