Distribuição aleatória ponderada contínua, enviesada em direção a uma extremidade


28

Atualmente, estou contribuindo para um sistema de partículas para o nosso jogo e desenvolvendo algumas formas de emissor.

Minha distribuição aleatória uniforme ao longo de uma linha ou ao longo de uma área retangular funciona bem - não há problema.

Mas agora eu gostaria de ter algo como um gradiente unidimensional nesta distribuição. Isso significaria, por exemplo, valores mais baixos são mais comuns que valores mais altos.

Não sei quais seriam os termos matemáticos apropriados para esse problema; portanto, minhas habilidades de pesquisa são bastante inúteis para esse problema. Eu preciso de algo que seja computacionalmente simples, pois o sistema de partículas precisa ser eficiente.



Ninguém vai mencionar cálculo?
Alec Teal

Respostas:


42

Dê uma olhada nesta foto:

Mapeamento de curvas

Ele mostra o processo de mapeamento de um valor (aleatório) para uma curva. Suponha que você gere um valor aleatório uniformemente distribuído X, variando de 0 a 1. Mapeando esse valor para uma curva - ou, em outras palavras, usando f (X) em vez de X - você pode distorcer sua distribuição da maneira que desejar .

Nesta figura, a primeira curva aumenta a probabilidade de valores mais altos; o segundo aumenta a probabilidade de valores mais baixos; e o terceiro faz agrupar valores no meio. A fórmula exata da curva não é realmente importante e pode ser escolhida como você quiser.

Por exemplo, a primeira curva se parece um pouco com a raiz quadrada e a segunda com o quadrado. O terceiro é um pouco parecido com cubo, apenas traduzido. Se você considerar a raiz quadrada muito lenta, a primeira curva também se parecerá com f (X) = 1- (1-X) ^ 2 - uma inversão de quadrado. Ou uma hipérbole: f (X) = 2X / (1 + X).

Como mostra uma quarta curva, você pode simplesmente usar uma tabela de pesquisa pré-computada. É feio como uma curva, mas provavelmente será bom o suficiente para um sistema de partículas.

Essa técnica geral é muito simples e poderosa. Qualquer que seja a distribuição de que você precisa, imagine um mapeamento de curvas e criará uma fórmula rapidamente. Ou, se o seu mecanismo tiver um editor, faça um editor visual para a curva!


muito obrigado por sua explicação muito completa e compreensível. todas as outras postagens também foram muito úteis, mas eu realmente pude entender sua postagem da maneira mais fácil e rápida. ele se destacou porque realmente atingiu o ponto da minha maneira de entender as coisas. e os aspectos que você está explicando são exatamente o que eu estava procurando (ou vagando)! isso me permitirá usar isso em muitos casos no futuro. então thx novamente !!! btw, eu brinquei com algumas de suas curvas e funciona como charme.
Didito 24/05

5
FYI: Essas são chamadas funções quantílicas: en.wikipedia.org/wiki/Quantile_function
Neil G

8

Uma explicação mais longa:

Se você tem uma distribuição de probabilidade desejada , como o gradiente @didito solicitado, pode descrevê-lo como uma função. Digamos que você queira uma distribuição triangular, onde a probabilidade em 0 seja 0,0 e que você queira escolher um número aleatório de 0 a 1. Podemos escrever como y = x.

O próximo passo é calcular a integral desta função. Nesse caso, é . Avaliado de 0 a 1, isso é ½. Isso faz sentido - é um triângulo com base 1 e altura 1, então sua área é ½.x=1x2

Você escolhe um ponto aleatório uniformemente de 0 à área (½ no nosso exemplo). Vamos chamar isso de z. (Estamos escolhendo uniformemente a distribuição cumulativa .)

O próximo passo é voltar atrás, para descobrir qual valor de x (vamos chamá-lo de x̂) corresponde a uma área de z. Estamos procurando por , avaliado de 0 a x̂, sendo igual a z. Quando você resolve , obtém .x=1x21x̂2=zx̂=2z

Neste exemplo, você escolhe z de 0 a ½ e, em seguida, o número aleatório desejado é . Simplificado, você pode escrevê-lo como - exatamente o que o eBusiness recomendou.2zrand(0,1)


thx por sua entrada valiosa. Eu sempre gosto de ouvir como pessoas qualificadas resolvem problemas. Mas eu ainda preciso envolver minha cabeça em torno dele para ser honesto ...
didito

isso é incrível. Eu sempre fiz a sqrt(random())minha vida inteira, mas cheguei a ela empiricamente. Tentando amarrar um número aleatório a uma curva, e funcionou. Agora que tenho um pouco mais de matemática, saber por que funciona é muito valioso!
18135 Gustavo Maciel

5

Você provavelmente obteria uma aproximação aproximada do que deseja, utilizando um sistema exponencial.

Faça x baseado em algo como 1- (rnd ^ value) (supondo que rnd esteja entre 0 e 1) e você obterá alguns comportamentos diferentes da inclinação da esquerda para a direita com base no que você usa. Um valor mais alto proporcionará uma distribuição mais distorcida

Você pode usar uma ferramenta gráfica on-line para obter algumas idéias aproximadas sobre os comportamentos que as diferentes equações fornecerão antes de colocá-las, ou você pode simplesmente mexer nas equações diretamente no seu sistema de partículas, dependendo do estilo que mais lhe agrada.

EDITAR

Para algo como um sistema de partículas em que o tempo de CPU por partícula é muito importante, o uso do Math.Pow (ou equivalente de idioma) diretamente pode levar a uma diminuição no desempenho. Se desejar mais desempenho e o valor não estiver sendo alterado no tempo de execução, considere alternar para uma função equivalente, como x * x, em vez de x ^ 2.

(Os expoentes fracionários podem ser mais um problema, mas alguém com uma formação matemática mais forte do que eu provavelmente poderia ter uma boa maneira de criar uma função de aproximação)


11
Em vez de usar um programa gráfico, você pode plotar a distribuição Beta, pois esse é um caso especial. Para um dado value, este é Beta (valor, 1).
Neil G

THX. Eu tentei traçar alguns gráficos e acho que poderia me levar onde eu quero.
Didito 24/05

@Neil G obrigado pela dica com "distribuição beta" - isto soa interessantes e úteis ... eu vou fazer alguma pesquisa sobre o assunto
didito

3

O termo que você está procurando é que a Weighted Random Numbersmaioria dos algoritmos que vi usam funções trigonométricas, mas acho que descobri uma maneira que será eficiente:

Crie uma tabela / matriz / lista (qualquer que seja) que contenha um valor multiplicador para a função aleatória. Preencha-o manualmente ou programaticamente ...

randMulti= {.1,.1,.1,.1,.1,.1,.2,.2,.3,.3,.9,1,1,1,} 

... então multiplique randompor um escolhido aleatoriamente randMultie, finalmente, pelo valor máximo da distribuição ...

weightedRandom = math.random()*randMulti[Math.random(randMulti.length)]*maxValue

Acredito que isso será muito mais rápido do que o uso sqrtou outras funções computacionalmente complexas e permitirá padrões de agrupamento mais personalizados.


2
Se você puder sacrificar a memória, uma tabela de 100 valores pré-calculados seria mais rápida (e um pouco mais precisa). Duvido que o usuário consiga distinguir entre as versões completa e pré-calculada.
Daniel Blezek

@ Daniel seria mais rápido, mas com 100 valores aleatórios, é muito fácil ver padrões repetidos.
AttackingHobo

Só porque parece haver um padrão de repetição, não significa que não seja aleatório. A essência da aleatoriedade é sua imprevisibilidade, o que literalmente significa que, tanto quanto não se pode prever que não haverá um padrão, também não se pode prever que possa haver um (pelo menos por um curto período de tempo). Você precisará fazer alguns testes, mas se encontrar padrões com vários testes usando sementes diferentes, talvez seja necessário revisar seu algoritmo para gerar números pseudo-aleatórios.
Randolf Richardson

@AttackingHobo thx para esse truque. Eu gosto do uso de LUTs. e a fórmula é bastante fácil de entender. eu não pensava nisso dessa maneira antes. não vendo a madeira para as árvores ... :) também acho que padrões repetidos devem ser evitados, mas provavelmente não seriam reconhecidos nesse caso. ainda assim, pré-computar todos os valores prejudicaria a experiência visual. de qualquer maneira, thx por me lembrar que este é um fator a considerar sobre o tema da aleatoriedade ...
didito

também obrigado por trazer o termo "Números aleatórios" ponderados!
Didito 24/05

2

Eu acho que o que você pede é a distribuição alcançada usando uma função de raiz quadrada.

[position] = sqrt(rand(0, 1))

Isso fornecerá uma distribuição no campo de dimensão única, [0, 1]onde a probabilidade de uma posição é equivalente a essa posição, ou seja, uma "distribuição triangular".

Geração alternativa sem quadratura:

[position] = 1-abs(rand(0, 1)-rand(0, 1))

Uma raiz quadrada na implementação ideal é apenas alguns comandos de multiplicação e soma sem ramificações. (Veja: http://en.wikipedia.org/wiki/Fast_inverse_square_root ). Qual dessas duas funções é mais rápida pode variar dependendo da plataforma e do gerador aleatório. Em uma plataforma x86, por exemplo, seriam necessárias apenas algumas ramificações imprevisíveis no gerador aleatório para tornar o segundo método mais lento.


A probabilidade de uma posição não será igual à posição (isso é matematicamente impossível - trivialmente, o domínio e o alcance da função incluem 0,50 e 0,51), nem é uma distribuição triangular. ( en.wikipedia.org/wiki/Triangular_distribution )

11
Embora o sqrt forneça alguns padrões interessantes, os sistemas de partículas geralmente precisam ter muita CPU por partícula, então eu recomendaria evitar raízes quadradas (que são computacionalmente lentas) sempre que possível. Às vezes, você pode apenas pré-computá-las, mas isso pode fazer com que suas partículas tenham padrões visíveis ao longo do tempo.
Lunin 23/05

11
@ Joe Wreschnig, você leu o artigo da Wikipedia por conta própria, insere a = 0, b = 1, c = 1 na fórmula de geração e você obtém a fórmula no meu post.
Aaaaaaaaaaaa 23/05

3
@Lunin, por que você está reclamando da raiz quadrada quando recebe um expoente em sua resposta?
Aaaaaaaaaaaa 23/05

11
@Lunin: A teoria do desempenho é um campo bastante negligenciado, muito do que as pessoas pensam que sabem onde estão aproximadamente corretas há 30 anos, quando as ALUs eram muito caras e lentas. Mesmo a função expoente que você acabou de descobrir como uma função aritmética bastante lenta raramente é um pecador de desempenho altamente significativo. Ramificações (usando uma instrução if) e falhas de cache (a leitura de um dado que atualmente não reside no cache) geralmente custam mais desempenho.
Aaaaaaaaaaaa

1

Basta usar uma distribuição Beta:

  • Beta (1,1) é plano
  • Beta (1,2) é um gradiente linear
  • Beta (1,3) é quadrático

etc.

Os dois parâmetros de forma não precisam ser inteiros.


thx por sua ajuda. como mencionado acima, a distribuição beta parece interessante. mas ainda não consigo entender o conteúdo da página da Wikipedia. ou uma fórmula / código. bem, também não tenho tempo agora para investigar mais: si vê que o impulso tem código para distribuições beta, mas isso seria um exagero. bem, acho que preciso passar por isso primeiro e depois escrever minha própria versão simplificada.
Didito 24/05

11
@didito: Não é tão difícil. Você acabou de substituir sua uniform_generator()ligação por gsl_ran_beta(rng, a, b). Veja aqui: gnu.org/software/gsl/manual/html_node/…
Neil G

thx pela dica. Eu não uso GSL (na verdade, nunca ouvi falar sobre isso antes), mas boa chamada. vou verificar a fonte!
26611 didito

@didito: Nesse caso, eu iria com a solução de Lunin. Boa sorte.
Neil G

0

Ainda mais simples, dependendo da velocidade do seu gerador aleatório, você pode apenas gerar dois valores e fazer a média deles.

Ou, ainda mais simples, onde X é o resultado do RNG, em primeiro lugar double y = double(1/x);, x = y*[maximum return value of rng];. Isso ponderará os números exponencialmente para os números mais baixos.

Gere e calcule a média de mais valores para aumentar a probabilidade de obter valores mais próximos do centro.

Obviamente, isso só funciona para distribuições padrão de curvas em sino ou versões "dobradas" *, mas com um gerador rápido, pode ser mais rápido e mais simples do que usar várias funções matemáticas como o sqrt.

Você pode encontrar todo tipo de pesquisa sobre curvas de sino de dados. De fato, o Anydice.com é um bom site que gera gráficos para vários métodos de rolar dados. Embora você esteja usando um RNG, a premissa é a mesma, assim como os resultados. Portanto, é um bom local para ver a distribuição antes mesmo de codificá-la.

* Além disso, você pode "dobrar" a distribuição de resultados ao longo de um eixo, pegando o eixo e subtraindo o resultado médio e adicionando o eixo. Por exemplo, você deseja que valores mais baixos sejam mais comuns e digamos que você queira 15 como seu valor mínimo e 35 como seu valor máximo, um intervalo de 20. Portanto, você gera e calcula a média de dois valores com um intervalo de 20 ( duas vezes o intervalo que você deseja), o que fornecerá uma curva de sino centralizada em 20 (subtraímos cinco no final para alterar o intervalo de 20 para 40 para 15 para 35). Pegue os números gerados X e Y.

Número final,

z =(x+y)/2;// average them
If (z<20){z = (20-z)+20;}// fold if below axis
return z-5;// return value adjusted to desired range

Se zero é o seu mínimo, melhor ainda, faça isso em vez disso,

z= (x+y)/2;
If (z<20){z = 20-z;}
else {z = z - 20;}
return z;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.