Modificar funções de distribuição aleatória :: Reduza a probabilidade de obter vários valores semelhantes em uma sequência

8

Quero gerar uma sequência de números para gerar planetas proceduralmente em um setor de galáxias. Cada planeta deve ser colocado aleatoriamente, no entanto, é muito improvável que dois planetas estejam diretamente próximos um do outro. Como posso conseguir isso?

Eu sei que você pode modificar as chances aplicando uma função de distribuição, mas como posso controlá-las para tornar valores específicos mais ou menos prováveis?

Gif mostrando a idéia de modificar uma curva de probabilidade dependendo dos valores já gerados.

API-Beast
fonte
2
Basta adicionar uma distância mínima para garantir que um planeta não esteja próximo de outro. Eu estimo isso é simples, então você poderia elaborar um pouco mais?
Madmenyo
@MennoGouw Sim, isso resolveria esse caso específico, embora eu queira melhorar meu entendimento de probabilidade, estou procurando uma solução "mais suave" sem limites rígidos / descartando números gerados.
API-Beast
Esclareça a solução "mais suave". É tudo sobre a definição de regras. Quando você precisa de certas regras para geração de procedimentos, precisa adicionar essas regras. Se você tiver casos especiais, defina mais ou diferentes regras para eles.
Madmenyo
Não sei por que você não usa apenas um gerador que tenha uma grande reputação em sua distribuição? (Acho que o twister Mersenne não é ruim.)
Vaillancourt
2
Concordo. A geração aleatória em si não é o problema. Fazer isso pode até quebrar seu gerador aleatório, tornando-o previsível. A geração de regras é o caminho a percorrer.
precisa saber é o seguinte

Respostas:

8

Se você conhece a distribuição que deseja, pode usar a amostragem por rejeição .

Maneira mais simples: no gráfico acima, escolha pontos aleatoriamente até encontrar um abaixo da curva. Em seguida, basta usar o x-coordenate.

Para a distribuição real, existem várias abordagens plausíveis. Por exemplo, para o número do planeta ino local pe alguns parâmetros de força k(por exemplo 0.5), defina uma função f_i(x)=abs(p-x)^ke use a função de distribuição g(x)=f_1(x)*f_2(x)*...*f_n(x).

Na prática, calcule e armazene os resultados de g(x)para array t( t[x]=g(x)); lembre-se do valor mais alto visto htambém. Escolher uma posição aleatória xno t, escolher um valor aleatório yentre 0e h, repetição if y>t[x]; caso contrário, o valor de retorno é x.

yarr
fonte
Você poderia se aprofundar um pouco mais na definição da função de distribuição? O resto deve ser bem claro.
API-Beast
Por exemplo, se os planetas atuais estiverem nas posições 0,1, 0,3 e 0,8, g (x) = (abs (x-0,1) * abs (x-0,3) * abs (x-0,3) * abs (x-0,8)) ^ 0,5, onde "^" significa exponenciação. (Isso é escrito de maneira um pouco diferente da fórmula anterior, mas equivalente.) Essa função de distribuição se parece com o gif da sua pergunta e não se baseia em nada em particular. (String de consulta para WolframAlpha: "plot de 0 a 1 (abs (x-0,1) * abs (x-0,3) * abs (x-0,8)) ^ 0,5")
yarr
Uau, essa função é bem legal. Não sabia que uma função como essa é realmente tão simples :) Link para o preguiçoso: bit.ly/1pWOZMJ
API-Beast
1

Não tenho certeza de que o problema esteja totalmente especificado pela pergunta, mas posso fornecer algumas idéias simples, a segunda delas fornecerá números aproximadamente de acordo com o que sua imagem indica que você deseja.

De qualquer maneira, como você pode perceber, a função de distribuição está mudando após cada número gerado e possui uma memória (isto é: não é markoviana ) e qualquer um desses métodos pode ser impraticável quando a 'memória' (número de números previamente sorteados) é muito grande.

  1. Simples:
    Gere números aleatórios a partir de uma distribuição plana, compare com números previamente desenhados e repita se estiver "muito próximo"

  2. Esta resposta é mais parecida com a sua figura (supondo que desejemos desenhar de 0..1):

    • crie uma nova lista ordenada, insira 0 e 1
    • gerar número aleatório a partir de uma função de distribuição plana: N_0
      • adicione este número à lista
    • na próxima chamada, desenhe outro número N_1,
    • se N_1> N_0
      • desenhe um novo número aleatório gaussiano com média = 1 e um desvio padrão de o que você quiser, um número menor (comparado com 1-N_1) manterá os números aleatórios mais espaçados. Isso não garantirá uma distância mínima entre empates, mas, novamente, sua figura também não parece.
    • caso oposto de N_1 <N_0 tratado de forma semelhante
    • em empates subsequentes, continue gerando um número aleatório (N_i) a partir de uma distribuição plana
    • percorra sua lista para ver quais dois números sorteados anteriormente o novo número se encontra (N_-, N_ +)
    • crie um novo número aleatório gaussiano com média (N_- + N _ +) / 2
    • adicione o número de distribuição plano (N_i) à sua lista (lista ordenada)

as caixas de terminais são um caso especial, mas deve ser simples o suficiente para você ver como lidar com elas.

Adam V. Steele
fonte
0

Pense na diferença entre 1 dado e 3 dados . 1 dado fornece uma probabilidade uniforme para todos os valores, enquanto 3 dados tendem a ter uma probabilidade maior para os valores no meio.

Quanto mais "dados" na sua equação, maior a sua chance de conseguir algo em direção ao centro. Então, vamos definir uma função que possa lidar com qualquer número uniformemente :

// Takes a random number between floor and ceil
// pow defines how strongly these results should gravitate towards the middle
// We also define a function TrueRand(floor, ceil) elsewhere where you should substitute your own random function
int CenterRandom(int floor, int ceil, int pow = 3)
{
    if(ceil == floor)
        return ceil; // don't care to compare

    int total = 0;
    for(int x = 0; x < pow; x++)
    {
       total += TrueRand(floor, ceil);
    }
    return total / pow;
}

Agora podemos definir um exemplo de função para usar isso:

// Distribues a number of points between floor and ceil
// We assume a function PlotPoint(int) exists to aid in creating the planet, etc...
void DistributePoints(int floor, int ceil, int numPoints)
{
    // Could easily output this in the function parameters, but language wasn't specified
    int[numPoints] breaks;
    int numBreaks = 0;

    // Special case for first pair
    breaks[0] = CenterRandom(floor, ceil);
    numBreaks++;

    for(int x = 0; x < numPoints - 1; x++)
    {
        // Generate a random number linearly, this will be used for picking
        // This way we have a greater chance of choosing a random value between larger pairs
        int picker = TrueRandom(floor, ceil);

        // Now we first find the pair of points that our picker exists on
        // For simplicity, we handle the first and last pair separately

        if(picker >= floor && picker < breaks[0])
        {
            breaks[x] = CenterRandom(floor, breaks[0] - 1);
        }
        for(int i = 0; i < numBreaks; i++)
        {
            if(picker > breaks[i] && picker < breaks[i+1])
            {
                breaks[x] = CenterRandom(breaks[i] + 1, breaks[i+1] - 1);
            }
        }
        if(picker > breaks[numBreaks] && picker <= ceil)
        {
            breaks[x] = CenterRandom(breaks[numBreaks] + 1, ceil);
        }

        PlotPoint(breaks[x]); // Plot the point
    }
}

Agora, o primeiro a observar é que esse código realmente não verifica se o selecionador já corresponde a um dos pontos. Se isso acontecer, simplesmente não irá gerar um ponto, possivelmente algo que você possa gostar.

Para explicar o que está acontecendo aqui, o CenterRandom gera uma espécie de curva de sino. Essa função divide o plano em várias curvas de sino, uma por par de pontos existentes. O selecionador nos diz de qual curva de sino gerar. Como escolhemos linearmente, podemos garantir que os pares com intervalos maiores sejam escolhidos com mais frequência, mas ainda o deixamos completamente aleatório.

Espero que isso aponte a direção certa.


fonte
0

Sei que você está perguntando sobre uma sequência de posições aleatórias, mas se você não está restrito a gerar o conjunto sequencialmente, há outra abordagem: gerar um conjunto de pontos com o espaçamento desejado.

O que eu acho que você deseja é um conjunto de planetas razoavelmente espaçados com alguma aleatoriedade. Em vez de gerar posições do planeta com um gerador de números aleatórios, gere o espaçamento do planeta com um gerador de números aleatórios. Isso permitirá que você controle diretamente a distribuição do espaçamento, usando um gerador de números aleatórios que escolhe essa distribuição. Isso é direto em uma dimensão.

Em duas dimensões, vi algumas abordagens que geram "ruído azul", mas não sei como gerar espaçamento com uma distribuição arbitrária. Este artigo aborda a abordagem padrão "tente colocá-lo e rejeite se estiver muito próximo", mas você pode gerá-los todos de uma vez, com uma solução "mais suave", colocando todos os seus pontos e, em seguida, usando o Lloyd Relaxation para mover todos os planetas para mais posições desejáveis. Isso afastará os planetas muito próximos. Wang Recursive Tiles é outra abordagem que pode ser útil. Este papelestende o problema para gerar planetas com uma densidade e algum outro objeto como asteróides com outra densidade. Você também pode gerar ruído azul usando a série Fourier; Não tenho certeza. A abordagem da série Fourier também permite que você use distribuições arbitrárias em vez de apenas ruído azul.

amitp
fonte