views:

153

answers:

2

I've been working on generating Perlin noise for a map generator of mine. The problem I've run into is that the random noise is not distributed normally, and is more likely a normal distribution of kinds.

Given two integers X and Y, and a seed value, I do the following:

  • Use MurmurHash2 to generate a random number (-1,1). This is uniformly distributed.
  • Interpolate points between integer values with cubic interpolation. Values now fall in the range (-2.25, 2.25) because the interpolation can extrapolate higher points (by 1.5 in every dimension) between similar values, and the distribution is no longer uniform.
  • Generate these interpolated points, summing them together while halving the amplitudes (See: Perling noise) As the number of sums approaches infinity, the limit of the range now approaches twice the previous values, or (-4.5, 4.5) and is now even less uniform.

This obviously doesn't work when I want a range from (-1, 1), so I divide all final values by 4.5. Actually, I divide them along the way (by 1.5 after interpolating each dimension, then by 2 after summing the noise.)

After the division, I'm left with a theoretical range of (-1, 1). However, the vast majority of the values are (-0.2,0.2). This doesn't work well when generating my maps, since I need to determine the percentage of the map filled. I also cannot use histograms to determine what threshold to use, since I'm generating the squares on demand, and not the entire map.

I need to make my distribution uniform at two points - after the interpolation, and after the summing of the noise functions. I'm not sure how to go about this, tho.

My distribution looks like this:

Normal distribution

I need it to look like this:

Uniform distribution

(Both images from Wikipedia.)

Any solutions are appreciated, but I'm writing in C#, so code snippets would be extremely helpful.

A: 

This is a simple scaling problem. And all simple scaling problems are just manifestations of a straight line in Cartesian geometry:

You have a line:

       |   /
     1 + -/,
       | / ,
____,__|/__,____
 -4.5 /|   4.5
    ,/ |
    /- + -1
   /   |

on that line, when x=4.5, y=1 and when x=-4.5 y=-1. Now I'm sure that once you realize this you know the solution. y=mx + c. Since the line is symmetric on both positive and negative sides then you can assume that it crosses at zero so c=0. Now to find the slope:

m = dy/dx
m = (1 - -1)/(4.5 - -4.5)
m = 2/9

therefore:

y = 2/9 x + 0
y = 2x / 9

so, now you can plug this in. What is y when x = 3?:

y = 2*3 / 9
y = 6/9
y = 2/3

and what is y when x = 4?:

y = 2*4 / 9
y = 8/9

additional notes:

The assume-the-line-crosses-at-zero thing I'm doing because my experienced eye tells me it's right. But if I were doing this for a high school math exam I'd likely lose credits (even if my answer is right). For the proper formulaic solution, to find c you have to first find m and then substitute the x and y values of a known coordinate:

y = 2/9 x + c

given that (4.5,1) and (-4.5,-1) are known coordinates, substitute x and y for 4.5 and 1:

1 = 2*4.5/9 + c
1 = 9/9 + c
1 = 1 + c
c = 1 - 1
c = 0

All this can be enshrined in a scaling function:

// example code in javascript:
function makeScaler (x1, y1, x2, y2) {
    var m = (x2-x1)/(y2-y1);
    var c = y1 - m*x1;

    // return a scaling function:
    return function (x) {
        return m*x + c;
    }
}

var f = makeScaler(-4.5,-1,4.5,1);
alert(f(4)); // what y is when x is 4

// or if you prefer:
var g = makeScaler(-4.5,0,4.5,1); // scale from 0 to 1
alert(g(4));
slebetman
This does not answer my question. Were the distribution already uniform (a straight line) then I wouldn't have a problem. I'm trying to force a curvy line onto a straight one.
Daniel Rasmussen
@Daniel A picture of the transformation you are looking for could be useful
belisarius
@belisarius - I'm not actually sure how I would get a picture of the distribution. Basically, I'm just looking for a way to scale a normal distribution into a uniform one.
Daniel Rasmussen
This answers your question. The straight line is NOT the DISTRIBUTION but the MAPPING. MAPPINGS are linear.
slebetman
Let's try this before you refuse to believe my correct answer: generate the distribution and dump it to a file. Then apply the linear mapping function and dump it to another file. Plot it. You will see that the original distribution and the linearly scaled distribution are the same except for the values being scaled.
slebetman
If I understand you correctly, scaling my curve will just give me a smaller curve. Of course, it's possible I don't understand you correctly. But I'm trying pretty hard...
Daniel Rasmussen
Yes. All simple scaling functions describe a straight line in cartesian coordinates. Take for example the volume knob on your radio. It applies a linear scaling to the audio waveform. But just because the scaling is linear it doesn't mean you get a different song when you turn the volume knob. Or take for example the phrase "halve the output values". Most will understand this to mean applying a scaling factor of 0.5 to something. What some fail to see is that the application of the scaling factor also describes a straight line in Cartesian space: `y=x/2+0`.
slebetman
...But I *want* a different song. I'm not turning the volume knob, I'm messing with the equalizer.
Daniel Rasmussen
No, you've **already** messed with the equalizer. And you've already found the filter you want (see your original code above). But after messing with the equalizer you've found the volume too loud. I'm trying to show you how to **then** use the volume knob to lower the volume.
slebetman
@slebetman @Daniel ... I'm following this discussion trying to understand the question ... and I'm failing. I guess it's time to rework the question :)
belisarius
I've attempted to reword my question in a more stats-oriented approach here: http://stats.stackexchange.com/questions/2617/how-can-i-determine-the-best-fit-normal-distribution-from-this-information
Daniel Rasmussen
@slebetman - Alright, I'm 99% sure this does not do what I want. It's a good answer for linear scaling, but I'm looking for a more complicated transformation. It's entirely possible I may need to rephrase my question somehow, but this is not the answer.
Daniel Rasmussen
+2  A: 

Combine the resulting sample with the CDF for the gaussian, which is 0.5*erf(x) + 1 (erf = error function).

Note that in virtue of the Central Limit Theorem, whenever you make sums of random variables, you get gaussian laws.

Alexandre C.
I think this is the right answer, but I'm having trouble coming up with the error function. The mean is obviously zero, but how do I determine the variance? I know the middle 50% of my distribution falls between about (-.4,4), with a maximum value at 1.672 and a minimum at -1.734 (calculated after taking a billion samples.)
Daniel Rasmussen
cdf = 0.5 + 0.5 * erf((x - mu) / (sqrt(2 * variance))). You have to determinate the variance empirically (google empirical variance estimator)
Alexandre C.