0

I have got an arbitary number of perlin noise maps and a weight for each. The sum of all weights is 1, but that shouln't make a difference.

I want to get that noise with the highest value regarding the weight.

My first approach was to get the numbers from each perlin noise, convert these to percentages (I've got a loopup table therefor), multiply with the weight and select the hightest value. But this approach has a huge flaw: It discriminates smaller weights and favores the larger ones and so messes up the distribution. I want a weight of 0.2 to appear in 20%.

I want to use this for a map generator to select a tile type. I use multiple perlin noises because I want to use many different tile types to border on each other and thus can't use a gradient.

Does anyone know how to fix this flaw, or a different way to generate tile based maps?

EDIT: Meanwhile I figured out a nearly acceptable solution:

final float[] values = new float[noises.length];
for (int i = 0; i < values.length; i++)
    values[i] = noises[i].get(x, y) * (1f + (weights[i] - 1f / noises.length));

int max = 0;
for (int i = 1; i < values.length; i++)
    if (values[i] > values[max])
        max = i;
return noises[max];

It's only flaw is being imprecise. Even with a weight of 0f a noise will still be retured in about 6% of all times.

DiddiZ
  • 625
  • 8
  • 17
  • What exactly do you mean by the "highest value regarding the weight"? When I first read that I assumed that you *wanted* to discriminate against small-weight noise maps. – Russell Zahniser Mar 21 '13 at 18:26
  • Something like perlin value * weight and then select the largest. But I also want each one to selected equal to the weight, eg the noise with the weight 0.2 shall be the higtest 20% of the time. – DiddiZ Mar 21 '13 at 18:36

2 Answers2

1

Perhaps the solution would be something like this:

Perlin pickOne(Perlin[] noise, double[] weights) {
   double sum = 0;
   for(double weight : weights) sum += weight;
   double choice = Math.random() * sum;
   for(int i = 0; i < weights.length; ++i) {
      choice -= weights[i];
      if(choice <= 0) {
         return noise[i];
      }
   }
   throw new IllegalArgumentException("Must have at least one weight!");
}

(That is to say, select one of the noise maps based entirely on the weights, not the map values. What I'm doing to select it here could be imagined as laying out all the weights end-to-end on a number line, picking a value within them, and then figuring out which weight it falls within.)

Russell Zahniser
  • 16,188
  • 39
  • 30
0

"My first approach was to get the numbers from each perlin noise, convert these to percentages (I've got a loopup table therefor), multiply with the weight and select the hightest value. But this approach has a huge flaw: It discriminates smaller weights and favores the larger ones and so messes up the distribution. I want a weight of 0.2 to appear in 20%."

What I would suggest is:

1) Add up the sum of all percentages*weights

2) Select a random number between 0 and sum

3) Enumerate adding percentage*weight items until you exceed your random number - pick that

This will do a random distribution over all possibilities that is weighted by their percentage*weight, so if you have a 2, an 8 and a 6 the 2 appears 2/16 of the time, the 8 8/16 of the time and the 6 6/16 of the time.

Patashu
  • 21,443
  • 3
  • 45
  • 53