Modifique las funciones de distribución aleatoria :: hace que sea menos probable obtener múltiples valores similares en una secuencia

8

Quiero generar una secuencia de números para generar planetas procesales en un sector de galaxias. Cada planeta debe colocarse al azar, sin embargo, es muy poco probable que dos planetas estén directamente uno al lado del otro. ¿Cómo puedo lograr eso?

Sé que puede modificar las posibilidades aplicando una función de distribución, pero ¿cómo puedo controlarlas para hacer que los valores específicos sean más / menos probables?

Gif que muestra la idea de modificar una curva de probabilidad en función de los valores ya generados.

API-Bestia
fuente
2
Simplemente agregando una distancia mínima se aseguraría de que un planeta no esté al lado de otro. Calculo que esto es demasiado simple, ¿podría elaborar más?
Madmenyo
@MennoGouw Sí, eso lo resolvería para este caso específico, aunque quiero mejorar mi comprensión de la probabilidad, así que estoy buscando una solución "más suave" sin límites duros / descartando los números generados.
API-Beast
Aclare la solución "más suave". Se trata de establecer reglas. Cuando necesita ciertas reglas para la generación de procedimientos, debe agregar estas reglas. Si tiene casos especiales, establezca más o diferentes reglas para estos.
Madmenyo
No estoy seguro de por qué no solo usas un generador que tiene una gran reputación sobre su distribución. (Creo que el tornado de Mersenne no es malo.)
Vaillancourt
2
Estoy de acuerdo. La generación aleatoria en sí no es el problema. Hacer esto puede incluso romper tu generador aleatorio haciéndolo predecible. La generación de reglas es el camino a seguir.
ashes999

Respuestas:

8

Si conoce la distribución que desea, puede usar el muestreo de rechazo .

La forma más simple: en el gráfico anterior, elija puntos al azar hasta que encuentre que uno está debajo de la curva. Entonces solo usa el x-coordinado.

Para la distribución real, hay varios enfoques plausibles. Por ejemplo, para el número de planeta ien la ubicación py algún parámetro de fuerza k(por ejemplo 0.5), defina una función f_i(x)=abs(p-x)^ky luego use la función de distribución g(x)=f_1(x)*f_2(x)*...*f_n(x).

En la práctica, calcule y almacene los resultados de g(x)a array t( t[x]=g(x)); recuerde el valor más alto visto htambién. Elija una posición aleatoria xen t, elija un valor aleatorio yentre 0y h, repita if y>t[x]; de lo contrario, el valor de retorno es x.

Yarr
fuente
¿Podría profundizar un poco más sobre la definición de la función de distribución? El resto debería quedar bastante claro.
API-Beast
Por ejemplo, si los planetas actuales están en las posiciones 0.1, 0.3 y 0.8, g (x) = (abs (x-0.1) * abs (x-0.3) * abs (x-0.8)) ^ 0.5, donde "^" significa exponenciación. (Esto está escrito de manera ligeramente diferente de la fórmula anterior, pero es equivalente.) Esta función de distribución se parece más o menos al gif en su pregunta y no se basa en nada en particular. (Cadena de consulta para WolframAlpha: "trama de 0 a 1 (abs (x-0.1) * abs (x-0.3) * abs (x-0.8)) ^ 0.5")
yarr
Wow, esa función es genial. No sabía que una función como esa es realmente así de simple :) Enlace para los perezosos: bit.ly/1pWOZMJ
API-Beast
1

No estoy seguro de que la pregunta especifique completamente el problema, pero puedo proporcionar algunas ideas simples, la segunda de ellas proporcionará números aproximadamente de acuerdo con lo que su imagen indica que desea.

De cualquier manera, como puede darse cuenta, la función de distribución está cambiando después de cada número generado y tiene una memoria (es decir, no es de Markovian ) y cualquiera de estos métodos puede resultar poco práctico cuando la 'memoria' (número de números extraídos previamente) muy grande.

  1. Simple:
    genere un número aleatorio a partir de una distribución plana, compárelo con números dibujados previamente, repita si está 'demasiado cerca'

  2. Esta respuesta es más parecida a su figura (suponiendo que queremos extraer de 0..1):

    • cree una nueva lista ordenada, inserte 0 y 1
    • generar un número aleatorio a partir de una función de distribución plana: N_0
      • agrega este número a la lista
    • en la próxima llamada, dibuje otro número N_1,
    • si N_1> N_0
      • dibuje un nuevo número aleatorio gaussiano con media = 1 y una desviación estándar o de lo que desee, un número menor (en comparación con 1-N_1) mantendrá los números aleatorios más separados. Esto no garantizará una distancia mínima entre los sorteos, pero de nuevo su figura tampoco lo parece.
    • caso opuesto de N_1 <N_0 manejado de manera similar
    • en los sorteos posteriores, siga generando un número aleatorio (N_i) a partir de una distribución plana
    • recorra su lista para ver qué dos números dibujados previamente se encuentran entre el nuevo número (N_-, N_ +)
    • crear un nuevo número aleatorio gaussiano con media (N_- + N _ +) / 2
    • agregue el número de distribución plana (N_i) a su lista (lista ordenada)

Los puntos finales son un caso especial, pero debería ser lo suficientemente simple para que usted vea cómo manejarlos.

Adam V. Steele
fuente
0

Piensa en la diferencia entre 1 dado y 3 dados . 1 dado le da una probabilidad uniforme para todos los valores, mientras que 3 dados tenderán a tener una probabilidad más alta para los valores hacia el medio.

Cuantos más "dados" haya en su ecuación, más posibilidades tendrá de obtener algo hacia el centro. Así que definamos una función que pueda manejar cualquier número de manera uniforme :

// 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;
}

Ahora podemos definir una función de ejemplo para usar esto:

// 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
    }
}

Ahora, lo primero en notar es que este código realmente no verifica si el selector ya coincide con uno de los puntos. Si lo hace, simplemente no va a generar un punto, posiblemente algo que le gustaría.

Para explicar lo que está sucediendo aquí, CenterRandom genera una especie de curva de campana. Esta función divide el plano en múltiples curvas de campana, una por par de puntos existentes. El selector nos dice desde qué curva de campana generar. Dado que elegimos linealmente, podemos asegurarnos de que los pares con espacios más grandes entre ellos se elijan con mayor frecuencia, pero aún lo dejamos completamente al azar.

Espero que esto te señale en la dirección correcta.


fuente
0

Sé que está preguntando acerca de una secuencia de posiciones aleatorias, pero si no está restringido a generar el conjunto secuencialmente, hay otro enfoque: generar un conjunto de puntos que tenga el espacio deseado.

Lo que creo que quieres es un conjunto de planetas que estén razonablemente espaciados con cierta aleatoriedad. En lugar de generar posiciones planetarias con un generador de números aleatorios, genere un espacio entre planetas con un generador de números aleatorios. Esto le permitirá controlar directamente la distribución del espaciado, mediante el uso de un generador de números aleatorios que selecciona de esa distribución. Esto es sencillo en 1 dimensión.

En 2 dimensiones, he visto algunos enfoques que generan "ruido azul", pero no conozco una forma de generar espacios con una distribución arbitraria. Este artículo cubre el enfoque estándar de "intenta colocarlo y rechaza si está demasiado cerca", pero puedes generarlos todos a la vez, con una solución "más suave" colocando todos tus puntos, luego usando Lloyd Relaxation para mover todos los planetas a más posiciones deseables Moverá los planetas demasiado cercanos más lejos. Los mosaicos Wang recursivos son otro enfoque que podría ser útil. Este papelextiende el problema a generar planetas con una densidad y algún otro objeto como los asteroides con otra densidad. También podría generar ruido azul utilizando la serie Fourier; No estoy seguro. El enfoque de la serie de Fourier también le permitiría usar distribuciones arbitrarias en lugar de solo ruido azul.

amitp
fuente