Eu estou olhando para implementar um sistema baseado em chance que é tendencioso por evento anterior.
Antecedentes: Alguns anos atrás, lembro-me de uma atualização para o World of Warcraft anunciando que eles implementavam uma nova calculadora de chances que contrariava cadeias pontuais de eventos. (por exemplo, fazendo ataques críticos ou esquivando-se várias vezes seguidas). A idéia era que, no caso de você desviar de um golpe, a chance de evitar o próximo golpe seria diminuída, mas funcionaria nos dois sentidos. Não se esquivar de um golpe aumentaria igualmente a chance de se esquivar do próximo golpe. O principal truque aqui foi que, ao longo de várias tentativas, a chance de esquiva ainda corresponderia à porcentagem dada ao jogador em sua ficha de estatísticas.
Esse tipo de sistema me intrigou muito na época, e agora estou na situação de precisar de uma solução desse tipo.
Aqui estão os meus problemas:
- Suponho que eu seria capaz de encontrar recursos on-line para implementar esse sistema, mas talvez eu não tenha as palavras relevantes para encontrá-lo.
- Também preciso dessa abordagem para ajustar um sistema que não seja binomial (ou seja, dois resultados), mas que contenha 4 eventos mutuamente exclusivos.
Minha abordagem atual é semelhante à de um sistema de tickets de rifa. Quando ocorre um evento, altero os pesos em favor de todos os outros eventos. Isso poderia funcionar se os quatro eventos fossem igualmente prováveis, mas, no meu caso, precisam ser muito mais prevalentes. Mas como o evento predominante acontece com mais frequência, ele altera os pesos do outro muito mais do que o pretendido e não consigo encontrar os números para as alterações de peso necessárias para manter a contagem média de tickets em torno dos valores iniciais em que o evento foi realizado. dado.
Alguns indicadores de direção ou um exemplo claro seriam muito apreciados.
Respostas:
Basicamente, o que você está pedindo é um gerador de eventos "semi-aleatório" que gere eventos com as seguintes propriedades:
A taxa média na qual cada evento ocorre é especificada antecipadamente.
É menos provável que o mesmo evento ocorra duas vezes seguidas do que aleatoriamente.
Os eventos não são totalmente previsíveis.
Uma maneira de fazer isso é primeiro implementar um gerador de eventos não aleatórios que satisfaça os objetivos 1 e 2 e, em seguida, adicionar alguma aleatoriedade para satisfazer o objetivo 3.
Para o gerador de eventos não aleatórios, podemos usar um algoritmo de pontilhamento simples . Especificamente, sejam p 1 , p 2 , ..., p n as probabilidades relativas dos eventos 1 a n e s = p 1 + p 2 + ... + p n seja a soma dos pesos. Em seguida, podemos gerar uma sequência de eventos máxima não-aleatória equidistribuída usando o seguinte algoritmo:
Inicialmente, deixe e 1 = e 2 = ... = e n = 0.
Para gerar um evento, incrementar cada e i por p i , ea saída do evento k para o qual e k é maior (quebrando os laços da maneira que quiser).
Decremento e k por s e repetição do passo 2.
Por exemplo, dados três eventos A, B e C, com p A = 5, p B = 4 ep C = 1, esse algoritmo gera algo como a seguinte sequência de saídas:
Observe como essa sequência de 30 eventos contém exatamente 15 As, 12 Bs e 3 Cs. Não é bastante otimizada distribui - há algumas ocorrências de dois Como em uma fileira, o que poderia ter sido evitado - mas chega perto.
Agora, para adicionar aleatoriedade a essa sequência, você tem várias opções (não necessariamente mutuamente exclusivas):
Você pode seguir o conselho de Philipp e manter um "deck" de N eventos futuros, para um número N de tamanho apropriado . Toda vez que você precisa gerar um evento, escolhe um evento aleatório no baralho e o substitui pela próxima saída de evento pelo algoritmo de pontilhamento acima.
A aplicação disso no exemplo acima, com N = 3, produz, por exemplo:
enquanto N = 10 produz a aparência mais aleatória:
Observe como os eventos comuns A e B terminam com muito mais execuções devido ao embaralhamento, enquanto os eventos C raros ainda são razoavelmente bem espaçados.
Você pode injetar alguma aleatoriedade diretamente no algoritmo de pontilhamento. Por exemplo, em vez de incrementar e i por p i na etapa 2, você pode incrementá-lo por p i × aleatório (0, 2), onde aleatório ( a , b ) é um número aleatório distribuído uniformemente entre a e b ; isso produziria uma saída como a seguinte:
ou você pode incrementar e i por p i + aleatório (- c , c ), o que produziria (para c = 0,1 × s ):
ou, para c = 0,5 × s :
Observe como o esquema aditivo tem um efeito aleatório muito mais forte para os eventos raros C do que para os eventos comuns A e B, em comparação com o evento multiplicativo; isso pode ou não ser desejável. Obviamente, você também pode usar alguma combinação desses esquemas ou qualquer outro ajuste nos incrementos, desde que preserve a propriedade de que o incremento médio de e i é igual a p i .
Como alternativa, você pode perturbar a saída do algoritmo de pontilhamento, substituindo algumas vezes o evento escolhido k por um aleatório (escolhido de acordo com os pesos brutos p i ). Desde que você também use o mesmo k na etapa 3 e emita na etapa 2, o processo de pontilhamento ainda tenderá a uniformizar as flutuações aleatórias.
Por exemplo, aqui está um exemplo de saída, com 10% de chance de cada evento ser escolhido aleatoriamente:
e aqui está um exemplo com 50% de chance de cada saída ser aleatória:
Também poderia considerar que alimenta uma mistura de eventos puramente aleatórios e pontilhadas numa plataforma / piscina de mistura, como descrito acima, ou talvez randomizar o algoritmo pontilhado escolhendo k aleatoriamente, como pesadas pelo e i s (tratamento de pesos negativos como zero).
Ps. Aqui estão algumas sequências de eventos completamente aleatórias, com as mesmas taxas médias, para comparação:
Tangente: Como houve um debate nos comentários sobre se é necessário, para soluções baseadas em decks, permitir que o deck esvazie antes de ser reabastecido, decidi fazer uma comparação gráfica de várias estratégias de preenchimento de decks:
Traçar várias estratégias para gerar lançamentos de moedas semi-aleatórios (com proporção de 50:50 de cara para coroa), em média. Eixo horizontal é o número de inversões, o eixo vertical é a distância cumulativa da razão esperada, medida como (cara - coroa) / 2 = cara - coroa / 2.
As linhas vermelha e verde no gráfico mostram dois algoritmos não baseados em baralho para comparação:
As outras três linhas (azul, roxo e ciano) mostram os resultados de três estratégias baseadas no baralho, cada uma implementada usando um baralho de 40 cartas, que é inicialmente preenchido com 20 cartas "chefes" e 20 cartas "caudas":
Obviamente, o gráfico acima é apenas uma realização de um processo aleatório, mas é razoavelmente representativo. Em particular, você pode ver que todos os processos baseados em baralho têm um viés limitado e permanecem razoavelmente perto da linha vermelha (determinística), enquanto a linha verde puramente aleatória acaba se afastando.
(De fato, o desvio das linhas azul, roxa e ciana do zero é estritamente limitado pelo tamanho do baralho: a linha azul nunca pode se afastar mais de 10 passos do zero, a linha roxa pode apenas 15 passos do zero e a linha ciana pode se afastar no máximo 20 passos do zero.Claro, na prática, qualquer uma das linhas que realmente atingem seu limite é extremamente improvável, pois há uma forte tendência para que elas retornem mais perto de zero se vagarem muito longe fora.)
À primeira vista, não há diferença óbvia entre as diferentes estratégias baseadas no baralho (embora, em média, a linha azul fique um pouco mais próxima da linha vermelha e a linha ciana fique um pouco mais distante), mas uma inspeção mais detalhada da linha azul revela um padrão determinístico distinto: a cada 40 desenhos (marcados pelas linhas verticais cinza pontilhadas), a linha azul encontra exatamente a linha vermelha no zero. As linhas roxa e ciana não são tão estritamente restritas e podem ficar longe de zero a qualquer momento.
Para todas as estratégias baseadas no baralho, o recurso importante que mantém a variação limitada é o fato de que, embora as cartas sejam retiradas aleatoriamente do baralho, o baralho é reabastecido deterministicamente. Se as cartas usadas para recarregar o baralho fossem escolhidas aleatoriamente, todas as estratégias baseadas no baralho se tornariam indistinguíveis da escolha aleatória pura (linha verde).
fonte
Não jogue dados, jogue cartas.
Pegue todos os resultados possíveis do seu RNG, coloque-os em uma lista, embaralhe-os aleatoriamente e retorne os resultados na ordem aleatória. Quando você estiver no final da lista, repita.
Os resultados ainda serão distribuídos uniformemente, mas os resultados individuais não serão repetidos, a menos que o último da lista também seja o primeiro do próximo.
Quando isso é um pouco previsível para o seu gosto, você pode usar uma lista com
n
o número de resultados possíveis e colocar cada resultado possível nelan
antes de embaralhar. Ou você pode reorganizar a lista antes que ela seja iterada completamente.fonte
Você pode tentar um gráfico aleatório de Markov . Considere cada evento que pode ocorrer como um nó em um gráfico. Em cada evento, faça um link para o outro evento que possa vir depois dele. Cada um desses links é ponderado por algo chamado probabilidade de transição . Em seguida, você executa uma caminhada aleatória do gráfico de acordo com o modelo de transição.
Por exemplo, você pode ter um gráfico que represente o resultado de um ataque (acerto crítico, esquiva etc.). Inicialize o nó inicial com um escolhido aleatoriamente, de acordo com as estatísticas do jogador (basta "rolar os dados"). Em seguida, no próximo ataque, decida o que acontecerá em seguida, dado o modelo de transição.
É necessário tomar cuidado para decidir como ponderar as transições. Por um lado, todas as transições que saem de um nó precisam somar uma probabilidade de 1. Uma coisa simples que você pode fazer é fazer uma transição de cada nó para outro nó, com pesos equivalentes à probabilidade de que esses eventos ocorram a priori , dado que o evento atual não pode ocorrer novamente.
Por exemplo, se você tiver três eventos:
Você pode configurar o modelo de transição para que um acerto crítico não ocorra novamente simplesmente redistribuindo sua massa de probabilidade para os outros eventos de maneira uniforme:
Edição: Como os comentários dizem abaixo, este modelo não é complicado o suficiente para obter o comportamento desejado. Em vez disso, pode ser necessário adicionar vários estados adicionais!
fonte
Aqui está uma implementação que eu criei em C # que:
Adicionei alguns comentários para que você possa ver o que estou fazendo.
Espero que isso ajude, por favor, sugerir melhorias para este código nos comentários, obrigado!
fonte
Deixe-me generalizar um pouco a resposta de mklingen . Basicamente, você deseja implementar a falácia do jogador , embora eu forneça um método mais geral aqui:
Digamos que haja
n
eventos possíveis com probabilidadesp_1, p_2, ..., p_n
. Quando o eventoi
aconteceu, sua probabilidade será redimensionada com um fator0≤a_i≤1/p_i
(o último é importante, caso contrário, você terá uma probabilidade maior que um e os outros eventos deverão ter probabilidades negativas , o que basicamente significa " anti ". Ou algo assim), embora tipicamentea_i<1
. Você pode, por exemploa_i=p_i
, escolher , o que significa que a probabilidade de um evento acontecer uma segunda vez é a probabilidade original de que o evento aconteça exatamente duas vezes seguidas, por exemplo, um segundo sorteio teria uma probabilidade de 1/4 em vez de 1/2. Por outro lado, você também pode ter algunsa_i>1
, o que significaria desencadear um "golpe de sorte / infortúnio".Todos os outros eventos devem permanecer igualmente prováveis em relação um ao outro, ou seja, todos devem ser redimensionados pelo mesmo fator
b_i
modo que a soma de todas as probabilidades seja igual a um, ou seja,Até agora, tão simples. Mas agora vamos adicionar outro requisito: Considerando todas as seqüências possíveis de dois eventos, as probabilidades de evento único extraídas delas devem ser as probabilidades originais.
Deixei
denote a probabilidade de um evento
j
acontecer após um eventoi
e observe que, ap_ij≠p_ji
menos queb_i=b_j (2)
(o que(1)
implicaa_j = 1 - a_i*p_i + (1-a_i)*p_i/p_j
). Isso também é o que o teorema de Bayes exige e também implicaapenas como desejado. Apenas observe como isso significa
a_i
corrige todos os outros.Agora vamos ver o que acontece quando aplicamos esse procedimento várias vezes, ou seja, para seqüências de três ou mais eventos. Existem basicamente duas opções para a escolha das probabilidades fraudulentas do terceiro evento:
a) Esqueça o primeiro evento e monte como se apenas o segundo tivesse ocorrido,
Observe que isso geralmente viola Bayes, uma vez que, por exemplo,
p_jik≠p_ikj
na maioria dos casos.b) Use as probabilidades
p_ij
(para fixasi
) como novas probabilidades api_j
partir das quais você obtém as novas probabilidadespi_jk
de que o eventok
aconteça a seguir. A decisão de modificarai_j
ou não depende de você, mas esteja ciente de que as novasbi_j
são definitivamente diferentes devido à modificaçãopi_j
. Por outro lado, a escolha deai_j
provavelmente é restrita, exigindo todas as permutações deijk
ocorrência com a mesma probabilidade. Vamos ver...e permutações cíclicas dos mesmos, que devem ser iguais para os respectivos casos.
Receio que minha continuação disso tenha que esperar um pouco ...
fonte
Eu acho que a melhor opção é usar a seleção de itens com peso aleatório. Há uma implementação para C # aqui , mas eles podem ser facilmente encontrados ou criados para outros idiomas.
A idéia seria reduzir o peso de uma opção toda vez que ela é escolhida e aumentá-la toda vez que não é escolhida.
Por exemplo, se você diminuir o peso da opção selecionada
NumOptions-1
e aumentar o peso de todas as outras opções em 1 (cuidado para remover itens com peso <0 e lê-los quando eles ultrapassarem 0) , todas as opções serão selecionadas aproximadamente o mesmo número de vezes durante um longo período, mas as opções escolhidas recentemente terão muito menos probabilidade de serem escolhidas.O problema do uso de uma ordem aleatória, conforme sugerido por muitas outras respostas, é que, após cada opção, mas uma foi escolhida, é possível prever com 100% de certeza qual opção será escolhida a seguir. Isso não é muito aleatório.
fonte
Minha resposta está incorreta, meu teste falhou.
Estou deixando esta resposta aqui para a discussão e comentários que apontam as falhas neste design, mas o teste real estava incorreto.
fonte
Você poderia fazer o que é essencialmente um filtro. Acompanhe os últimos n eventos. A probabilidade é de alguns filtros aplicados a esses eventos. O 0º filtro é a probabilidade básica, se 0, você se esquivou, se 1, você falhou. Digamos que a base seja de 25% e o filtro diminua pela metade a cada iteração. Seu filtro seria então:
Sinta-se livre para continuar, se desejar. A probabilidade geral desse esquema é um pouco maior que a probabilidade básica de 0,25. De fato, a probabilidade, dado o mesmo esquema, é (estou chamando x de probabilidade real, p é a entrada de probabilidade):
Resolvendo para x, encontra-se a resposta
p(1+1/2+1/4+1/8)/(1+p(1/2+1/4+1/8)
ou, para o nosso casox=0.38461538461
,. Mas o que você realmente quer é encontrar p, dado x. Isso acaba sendo um problema mais difícil. Se você assumiu um filtro infinito, o problema se tornax+x*p=2*p
, oup=x/(2-x)
. Assim, aumentando seu filtro, você poderia resolver um número p que, em média, fornecerá os mesmos resultados, mas a uma taxa dependente de quanto sucesso aconteceu recentemente.Basicamente, você usa os valores anteriores para determinar qual é o limite de aceitação nesta rodada e usa um valor aleatório. Em seguida, produza o próximo valor aleatório, considerando o filtro.
fonte
Assim como você se propôs, uma das abordagens para isso é implementar uma aleatória ponderada. A idéia é criar um gerador de número aleatório (ou resultado) onde pesos e resultados possam ser modificados.
Aqui está uma implementação disso em Java.
EDIT No caso em que você deseja ajustar os pesos automaticamente, por exemplo, aumente a chance de A quando o resultado for B. Você também pode,
nextOutcome()
método, para que ele modifique o peso de acordo com o resultadosetWeight()
para modificar o peso de acordo com o resultado.fonte