Create Gaussian distribution from random number generator output

913 Views Asked by At

I have a random number generator with output between -1 and +1 having a uniform distribution.

Is there a simple mathematical formula that would convert this into an estimated Gaussian distribution (around 0)?

3

There are 3 best solutions below

1
On

Usually a random sample from $U(0;1)$ is used. If you have a random sample from $U(-1;1)$ you can consider the absolute values of your output as from a $U(0;1)$

Then use the integral transform theorem

0
On

Another approach is to use Box–Muller transform. To be precise if $U_1$ and $U_2$ are independent samples from $U[-1,1]$, then

$$ Z_1 = \sqrt{-2\ln{|U_1|}} \cos(2\pi |U_2|), Z_2 = \sqrt{-2\ln{|U_1|}} \sin(2\pi |U_2|) $$ are independent standard normal random variables. It is slightly easier than using the integral transform in the context of simulating normal distribution (as integral transform uses the normal CDF which is more cumbersome). But remember that integral transform is applicable for any distribution!!

0
On

Standard normal sample in R. In R, the function rnorm (without extra parameters) gives numbers easily distinguished from a random sample from standard normal.

set.seed(2021)
z = rnorm(100)

The sample of 100 observations generated in this way passes the Shapiro-Wilk normality test, with P-value above 5%.

shapiro.test(z)$p.val
[1] 0.2116184

A normal probability plot (QQ-plot) is very nearly linear (ignoring a few wobbles in the tails, as usual).

qqnorm(z); qqline(z, col="green")

enter image description here

Standard normal sample from uniform data. In R the function runif without extra parameters gives uniformly generated pseudo-random numbers in $(0,1)$ Also, in R, the function qnorm provides an excellent rational approximatin to the inverse of the standard normal CDF. So the following R code also gives numbers that are difficult to distinguish from a random sample from standard normal.

set.seed(312)
u = runif(100)
z = qnorm(u)
shapiro.test(z)$p.val
[1] 0.4793456
qqnorm(z); qqline(z, col="green")

enter image description here

Using your pseudo-random numbers. You say your pseudorandom numbers are uniformly distributed in $(-1, 1).$ Such a sample of numbers v can be generated in R as below. Then by taking the absolute value as suggested by @tommik (+1) and using qnorm as above, you can get a sample that is not easy to distinguish from a random standard normal sample.

set.seed(1212)
v = runif(100, -1, 1)
summary(v)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.97538 -0.55026 -0.09479 -0.07188  0.32183  0.98725 
u = abs(v)
summary(u)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.01182 0.23112 0.44815 0.48566 0.77076 0.98725 
z = qnorm(u)
shapiro.test(z)$p.val
[1] 0.3778305
qqnorm(z); qqline(z, col="green")

enter image description here

Notes: In the early days of digital computers the following method of converting pseudo-random numbers on $(0,1)$ that uses only simple arithmetic was used. Sum 12 variates $U_i \stackrel{indep}{\sim}\mathsf{Unif}(0,1),$ then subtract 6. Convergence by the CLT of a sum of uniform distributions to normal is roughly satisfactory. The results are nearly normal but confined to $(-6,6).$

set.seed(12)
z = replicate(100, sum(runif(12))- 6)
shapiro.test(z)$p.val
[1] 0.2368584

For some years the Box-Muller method, suggested by @Suman (+1), was a standard method of generating standard normal deviates. However, double-precision arithmetic of the transcendental functions involved restrict the accuracy of the resulting 'standard normal' deviates beyond about $\pm 7.$. This inaccuracy is difficult to detect by most methods.

The inverse CDF of standard normal cannot be expressed directly but the rational approximation implemented in qnorm (due to Michael Wichura) is accurate within double-precision arithmetic.