Eu tenho um problema de amostragem simples, onde meu loop interno se parece com:
v = sample_gamma(k, a)
onde sample_gamma
amostras da distribuição Gamma para formar uma amostra de Dirichlet.
Funciona bem, mas para alguns valores de k / a, parte da computação a jusante é insuficiente.
Eu o adaptei para usar variáveis de espaço de log:
v = log(sample_gamma(k, a))
Depois de adaptar todo o restante do programa, ele funciona corretamente (pelo menos fornece os mesmos resultados exatos nos casos de teste). No entanto, é mais lento do que antes.
Existe uma maneira de amostrar diretamente sem usar funções lentas como log ( ) ? Tentei pesquisar no Google, mas nem sei se essa distribuição tem um nome comum (log-gama?).
sampling
gamma-distribution
luispedro
fonte
fonte
Respostas:
Considere-se uma pequena forma parâmetro perto de 0, tal como α = 1 / 100 . No intervalo entre 0 e α , e - α é aproximadamente 1 , então o Gamma pdf é aproximadamente x α - 1 d x / Γ ( α ) . Isso pode ser integrado a um CDF aproximado, F α ( x ) = x αα α=1/100 α e−α 1 xα−1dx/Γ(α) . Ao invertê-lo, vemos umapotência1/α: um enorme expoente. Paraα=1/100isso provoca alguma possibilidade de underflow (um valor de precisão dupla inferior a10-300, mais ou menos). Aqui está um gráfico da chance de ficar abaixo do fluxo em função do logaritmo da base dez deα:Fα(x)=xααΓ(α) 1/α α=1/100 10−300 α
Uma solução é explorar essa aproximação para gerar variáveis logarítmicas (gama): na verdade, tente gerar uma variável gama e, se for muito pequena, gere seu logaritmo a partir dessa distribuição aproximada de energia (como mostrado abaixo). (Faça isso repetidamente até que o log esteja dentro do intervalo de subfluxo, de modo que seja um substituto válido para a variável original de subfluxo.) Para o cálculo do Dirichlet, subtraia o máximo de todos os logaritmos de cada um dos valores do log: a gama varia para não afetar os valores do Dirichlet. Trate qualquer log resultante que seja muito pequeno (digamos, menor que -100) como sendo o log de um zero verdadeiro. Exponencie os outros logs. Agora você pode prosseguir sem fluxo insuficiente.
Isso vai demorar ainda mais do que antes, mas pelo menos funcionará!
Para gerar um log aproximado gama variável com o parâmetro de forma , pré-calcule C = log ( Γ ( α ) ) + log ( α ) . Isso é fácil, porque existem algoritmos para calcular os valores do log Gamma diretamente . Gere uma flutuação aleatória uniforme entre 0 e 1, pegue seu logaritmo, divida por α e adicione C a ele.α C=log(Γ(α))+log(α) α C
Como o parâmetro de escala apenas redimensiona a variável, não há problema em acomodá-lo nesses procedimentos. Você nem precisa se todos os parâmetros de escala forem iguais.
Editar
Em outra resposta, o OP descreve um método no qual a potência de uma variável uniforme (uma variável B ( α ) ) é multiplicada por uma variável Γ ( α + 1 ) . Isso funciona porque o pdf da distribuição conjunta dessas duas variáveis é igual a ( α x α - 1 ) ( y α e - y d y / Γ ( α + 1 ) ) . Para encontrar o pdf de z = x y1/α B(α) Γ(α+1) (αxα−1)(yαe−ydy/Γ(α+1)) z=xy substituímos , dividimos pelo x jacobino e integramos x . A integral deve variar de z a ∞ porque 0 ≤ y ≤ 1 , de ondey→z/x x x z ∞ 0≤y≤1
que é o pdf de uma distribuição .Γ(α)
O ponto principal é que, quando , é improvável que um valor extraído de Γ ( α + 1 ) seja insuficiente e somando seu log e 1 / α vezes o log de uma variável uniforme independente, teremos o log de um Varia ( α ) variável. É provável que o log seja muito negativo, mas teremos contornado a construção do seu antilog, que será transmitido em uma representação de ponto flutuante.0<α<1 Γ(α+1) 1/α Γ(α)
fonte
Estou respondendo minha própria pergunta, mas encontrei uma solução muito boa, mesmo que não a entenda completamente. Observando o código da GNU Scientific Library, eis como ele amostra variáveis gama (α β
r
é o gerador de números aleatórios,a
é e é β ):b
gsl_ran_gamma
gsl_rng_uniform_pos
_pos
Portanto, eu posso pegar o log da última expressão e usar
log()
pow()
fonte