Cálculo de un valor de probabilidad en el rango con límites mínimos / máximos

Think of a 2D grid, e.g. in the size of 1000x1000 cells, which is used as the map of a level in a game. This map is dynamically filled with game objects during runtime. Now we need to calculate the probability of placing a new object into the a given x/y position in this grid.

What I have already is an int array the holds the number of game objects in close distance to the cell at x/y. The index of this array represents the cell distance to the given cell, and each value in the array tells the number of game objects in the grid at that distance. So for example the array could look like this:

0, 0, 1, 2, 0, 3, 1, 0, 4, 0, 1

This would mean that 0 objects are in the grid cell at x/y itself, 0 objects are in the direct neighbour cells, 1 object is in a cell with a distance of two cells, 2 objects are in the cells of a distance of three cells, and so on. The following figure illustrates this example:

enter image description here

The task now is to calculate how likely it is to place a new object at x/y, based on the values in this array. The algorithm should be something like this:

  • if at least one object is already closer than min, then the probability must be 0.0
  • else if no object is within a distance of max, then the probability must be 1.0
  • else the probability depends on how many objects are close to x/y, and how many.

So in other words: if there is at least one game object already very close, we don't want a new one. On the other hand if there is no object within a max radius, we want a new object in any case. Or else we want to place a new object with a probability depending on how many other objects there are close to x/y -- the more objects are close, and the closer they are, the less likely we want to place a new object.

I hope my description was understandable.
Can you think of an elegent algorithm or formula to calculate this probability?

PS: Sorry for the title of this question, I don't know how to summarize my question better.

preguntado el 28 de mayo de 14 a las 13:05

Could you explain how you calculate the probability in the third case? Also, are these probabilities for accepting a chosen point? Like "I choose a random point on the grid. I accept it with a probability of p, and otherwise reject it and choose a new one."? Or are you generating an unnormalized likelihood distribution, from which you then sample cells? -

@Carsten: I don't generate these objects in one go. Instead during runtime I simple get two things: the x/y coordinates of one a grid cell, and the array described above. To your first question: As said the more objects close to x/y, and the closer they are, the less likely I want to create a new object. There are no more or more specific requirements. -

so you are happy with generating more objects in cells that happen to get calculated first? and that if your grid has edges (i.e. counts for the top left cell don't include objects from the bottom right) you will get more objects in corners and edges? -

@deathApril: Yes, at least for the first version this would be fine I guess. But of course: the more equal the distribution of the objects is in the end, the better :) -

5 Respuestas

One approach I'd consider is to compute a "population density" for that square. The lower the population density, the higher the probability that you would place an item there.

As you say, if there is an item at (x,y), then you can't place an item there. So consider that a population density of 1.0.

At the next level out there are 8 possible neighbors. The population density for that level would be n/8, Donde n is the number of items at that level. So if there are 3 objects that are adjacent to (x,y), then the density of that level is 3/8. Divide that by (distance+1).

Do the same for all levels. That is, compute the density of each level, divide by (distance+1), and sum the results. The divisor at each level is (distance*8). So your divisors are 8, 16, 24, etc.

Once you compute the results, you'll probably want to play with the numbers a bit to adjust the probabilities. That is, if you come up with a sum of 0.5, that space is likely pretty crowded. You wouldn't want to use (1-density) as your probability for generating an item. But the method I outline above should give you a single number to play with, which should simplify the problem.

So the algorithm looks something like:

total_density = 0;
for i = 0; i < max; ++i
    if (i == 0)
        local_density = counts[i]
    else
        local_density = counts[i]/(i*8);  // density at that level
    total_density = total_density + (local_density/(i+1))

If dividing the local density by (i+1) over-exaggerates the effect of distance, consider using something like log(i+1) or sqrt(i+1). I've found that to be useful in other situations where the distance is a factor, but not linearly.

contestado el 28 de mayo de 14 a las 15:05

Thanks for your detailed answer, that sounds like an interesting approach. One question though: how did you end up with ((distance+2)^2)-1 as the divisor? It sounds reasonable, but what is the thought behind that? - Matías

Downvoter: It's customary to provide a reason for the downvote. What specifically do you disagree with here? - jim mischel

but there are 16 cells at distance 2. the correct number of cells is distance*8 for distance > 0 (it is 1 for distance = 0, but no need to compute this if the cell itself is occupied) - Millón

@Matthias: See the update re: number of cells at distance d. - jim mischel

Thanks to both of you, it makes perfect sense now. - Matías

lets assume your array's name is distances.

double getProbability()
{
    for(int i=0 ; i<min ; i++)
    {
        if(distances[i]!=0) return 0;
    }

    int s = 0;
    bool b = true;
    for(int i=min ; i<max ; i++)
    {
        b = b && (distances[i]==0)
        s+= distances[i]/(i+1);
    }
    if(b) return 1;

    for(int i=0 ; i<distances.Count() ; i++)
    {
        s+= distances[i]/(i+1);
    }
    else return (float)s/totalObjectNum;
}

contestado el 28 de mayo de 14 a las 14:05

Thanks, but I think your solution does not take into account qué cerca the other objects are. As said one requirement is: the closer an object is to x/y, the less likely it is to place a new object. So we must respect the distance of the surrounding objects, not just their number. - Matías

@Matthias check out the new Edit. - Sachamora

This approach calculates a weighted sum of those objects in a distance > min and <= max. Parallel an upper limit is calculated (called normWeight) which depends only on max.

If at least one object is in a distance > min and <= max then the probability closest to 1 would be 1-(1/normWeight) for 1 object on the outer ring. The minimal probability would be 1-((normWeight-1)/normWeight). E.g. for max-1 objects on the outer ring.

The calculation of the weighted sum can be modified by calculating different values for the variable delta.

float calculateProbabilty()
{
    vector<int> numObjects; // [i] := number of objects in a distance i
    fill numObjects ....

    // given:
    int min = ...;
    int max = ...; // must be >= min


    bool anyObjectCloserThanMin = false;
    bool anyObjectCloserThanMax = false;

    // calculate a weighted sum

    float sumOfWeights  = 0.0;
    float normWeight    = 0.0;

    for (int distance=0; distance <= max; distance++)
    {
        // calculate a delta-value for increasing sumOfWeights depending on distance
        // the closer the object the higher the delta
        // e.g.: 
        float delta = (float)(max + 1 - distance);
        normWeight += delta;

        if (numObjects[distance] > 0 && distance < min)
        {
            anyObjectCloserThanMin = true;
            break;
        }

        if (numObjects[distance] > 0)
        {
            anyObjectCloserThanMax = true;
            sumOfWeights += (float)numObjects[distance] * delta;
        }
    }

    float probability = 0.0;

    if (anyObjectCloserThanMin)
    {
        // if at least one object is already closer than min, then the probability must be 0.0
        probability = 0.0;
    }
    else if (!anyObjectCloserThanMax)
    {
        //  if no object is within a distance of max, then the probability must be 1.0
        probability = 1.0;
    }
    else
    {
        // else the probability depends on how many objects are close to x/y

        // in this scenario normWeight defines an upper limited beyond that 
        // the probability becomes 0
        if (sumOfWeights >= normWeight)
        {
            probability = 0.0;
        }
        else
        {
            probability = 1. - (sumOfWeights / normWeight);
            // The probability closest to 1 would be 1-(1/normWeight) for 1 object on the outer ring.
            // The minimal probability would be 1-((normWeight-1)/normWeight). E.g. for
            // max-1 objects on the outer ring.
        }
    }

    return probability;
}

contestado el 28 de mayo de 14 a las 15:05

A simple approach could be:

1 / (sum over the number of all neighbours in [min, max] weighted by their distance to x/y + 1).

By weighted I mean that the number of those neighbours whose distance to x/y is smaller is multiplied by a bigger factor that the number of those, that are not so close. As weight you could for example take (max+1)-distance.

contestado el 28 de mayo de 14 a las 14:05

Note that once you compute the object density (ver "densidad de población"O"weighted sum of those objects in a distance" in previous answers), you still need to transform this value to a probability in which to insert objetos nuevos (which is not treated so comprehensivelly in other answers).

The probability function ((PDF)) needs to be defined for all possible values of object density, i.e. on closed interval [0, 1], but otherwise it can be shaped towards any goal you desire (ver ilustraciones), p.ej:

  • to move the current object density towards a desired maximum object density
  • a keep overall probability of insertion constant, while taking the local object density into account

insertion probability density functions

If you want to experiment with various goals (PDF function shapes - linear, quadratic, hyperbole, circle section, ...), you might wish to have a look at patrón de método de fábrica so you can switch between implementations while calling the same method name, but I wanted to keep things simpler in my example, so I implemented only the 1st goal (en python):

def object_density(objects, min, max):
    # choose your favourite algorithm, e.g.:
    #  Compute the density for each level of distance
    #  and then averages the levels, i.e. distance 2 object is
    #  exactly 8 times less significant from distance 1 object.
    #  Returns float between 0 and 1 (inclusive) for valid inputs.
    levels = [objects[d] / (d * 8) for d in range(min, max + 1)]
    return sum(levels) / len(levels)

def probability_from_density(desired_max_density, density):
    # play with PDF functions, e.g.
    #  a simple linear function
    #   f(x) = a*x + b
    #  where we know 2 points [0, 1] and [desired_max_density, 0], so:
    #      1 = 0 + b
    #      0 = a*desired_max_density + b
    #  Returns float betwen 0 and 1 (inclusive) for valid inputs.
    if density >= desired_max_density:
      return 0.0
    a = -1 / desired_max_density
    b = 1
    return a * density + b

def main():
    # distance 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    objects = [0, 0, 1, 2, 0, 3, 1, 0, 4, 0, 1]
    min = 2
    max = 5
    desired_max_density = 0.1

    if sum(objects[:min]):  # when an object is below min distance
      return 0.0 
    density = object_density(objects, min, max)  # 0,0552
    probability = probability_from_density(desired_max_density, density)  # 0,4479
    return probability

print(main())

contestado el 23 de mayo de 17 a las 13:05

note the python code is actually executable if someone want's to Juega con ello, but in Python 2.7 it requires from __future__ import division - Millón

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.