One can easily draw (pseudo-)random samples from a normal (Gaussian) distribution by using, say, NumPy:
import numpy as np
mu, sigma = 0, 0.1 # mean and standard deviation
s = np.random.normal(mu, sigma, 1000)
Now, consider the Fast Fourier transform of s
:
from scipy.fftpack import fft
sHat = fft(s)
Considering, presumably, generating Gaussian random numbers directly in the frequency domain might be more clever(?) (thus, efficient?) for various applications, and "the Fourier transform of white noise is white noise", and reportedly sHat
can be generated directly without the Fourier-transform of s
as theoretically shown herein;
Could you please advice novices (like myself) on how to implement such a useful idea? Favourably, a reference to an available implementation that I could not find in the web?
The following is my attempt to code the above theoretical explanation:
import numpy as np
from scipy.fftpack import ifft
N = 100
gaussComplex = np.full(shape=N, dtype=complex, fill_value=0.+0.j)
mu, sigma = 0, 1
s = np.random.normal(mu, sigma, N)
iters = np.arange(N) # 0..N-1
# 0..N/2-1
for i, item in enumerate(iters[:N/2]):
gaussComplex[i] = complex(s[i], s[i+N/2])
conjugateGaussComplex = np.conjugate(gaussComplex)
# N/2..N-1
for i, item in enumerate(iters[N/2:]):
gaussComplex[item] = conjugateGaussComplex[N-item]
sNew = ifft(gaussComplex)
A comparison of s
and sNew
reveals the following as I expect that they would be the same:
plt.plot(sHat.real, 'blue')
plt.plot(s, 'red')
the Fourier transform of white noise is white noise
This is true, but it does not mean that they are axactly the same - otherwise there would not be much point in doing the FFT.
If you plot s
and the real part of fft(s)
you will see that the transformed noise has much higher values.
Conversely, if you fill gaussComplex
with random values of standard deviation 1, the inversely transformed noise will have a much lower standard deviation. This is what you observe.
To generate the FFT of Gaussian white noise in the frequency domain you need to scale it correctly. In your case, this should do the trick:
gaussComplex *= np.sqrt(N/2)
You need to scale by sqrt(N)
because this is the normalization factor of the FFT. Furthermore, you also need to scale by 1/sqrt(2)
because you put noise with standard deviation of 1 in both, the real and imaginary part of the FFT. (However, the absolute value should have standard deviation of 1, hence the need to divide by sqrt(1+1)
.)
Plotting s
and sNew
with the correct scaling applied results in something like that:
The result is not exactly the same (as expected), but the noise varies in the same range - both have a standard deviation of ~1.