Edit: this answer is now structured in two sections. The first deals about creating a candidate RNG from audio data. The second demonstrates some testing I performed on this RNG.
Creating the RNG
Okay, I'll got at it another way then. I recorded 10 seconds of ambient noise on my MacBook Pro internal speakers. I was possibly in the worst conditions for this: my quiet flat, at night. The generated wav file was then imported into Mathematica. At least for my own combination of hardware, this doesn't look too good:
data = Import["test.wav", "Data"];
Length[data]
ListPlot[data[[1]]]
Histogram[data[[1]], PlotRange -> {{-0.0004, 0.0004}, Automatic}]
The data array has two components of length 520192 (it's 48 kHz audio, so it's indeed raw data). But, they take only a handful of possible values:
That being said, maybe some randomness can be extracted if the signal oscillate between these values in some random manner. If that's the case, I expect each value will only bring very little entropy to the result, but collectively you can still get something out. And indeed, the Fourier transform is:
ListPlot[Abs@Fourier[data[[1]]]]
which shows some promising behaviour. We take the mantissa, which is still very far from being uniformly distributed:
Histogram[(MantissaExponent[#][[1]] &) /@ Abs@Fourier[data[[1]]]]
and we can further refine by keeping only least-significant bits:
Histogram[(BitAnd[Floor[MantissaExponent[#][[1]]*2^32], 2^8 - 1] &) /@ Abs@Fourier[data[[1]]]]
Each integer in this list is between 0 and 255 (inclusive), so it's a 8-bit integer. They look nicely equidistributed, which of course is the lowest possible criterion for any kind of random generator. They should be further tested for randomness.
Alternatively, we can make it into a RNG that creates floating-point numbers between 0 and 1. The following is my “final state” code:
data = Import["test.wav", "Data"][[1]];
Print["Raw data length (one channel): ", Length[data]];
randombytes =
BitAnd[Floor[MantissaExponent[#][[1]]*2^32], 2^8 - 1] & /@
Abs@Fourier@data;
Print["Number of random bytes: ", Length[randombytes]];
randomint32s =
Table[randombytes[[i]] + randombytes[[i + 1]]*2^8 +
randombytes[[i + 2]]*2^16 + randombytes[[i + 3]]*2^24,
{i, 1, Length[randombytes], 4}];
randomfloats = N[#/2^32] & /@ randomint32s;
n = Length[randomfloats];
Print["Number of random reals: ", n];
Testing this RNG
I'm not an expert, so I performed so basic randomness tests following the guidelines in John D. Cook’s “Testing a Random Number Generator” chapter in Beautiful Testing. It's not DIEHARD, or DIEHARDER, but it's a start!
The approach I followed is to compare the properties of our RNG to those of streams of Mathematica’s default RNG (with the same size). I thus generate 100 vectors of reference random numbers:
references = Table[Table[RandomReal[], {i, n}], {j, 100}];
Then, I compare their properties. For example, I compare the average of randomfloats
to the distribution of averages of same-sized vectors returned by RandomReal
. For our RNG to be decent, our average must fit somewhere in the distribution of averages from RandomReal
, which I test by calculating the later’s standard deviation:
w = Mean[randomfloats]
t = Mean /@ references; Print[Min@t, " ", Mean@t, " ", Max@t, " ", StandardDeviation@t];
Print["DeltaMean over deviation: ", (w - Mean@t)/StandardDeviation@t];
which outputs:
0.499767
0.498 0.500117 0.502256 0.00081088
DeltaMean over deviation: -0.432063
so our result is at $-0.43\sigma$, and we can be happy about it! I did the same thing for the min ($-0.25\sigma$), max ($0.22\sigma$), and variance (slightly larger at $1.4\sigma$, but still no cause for concern). I skipped the book's bucket test, because we already established that using histograms.
Then, the Kolmogorov-Smirnov test:
Quiet@KolmogorovSmirnovTest[randomfloats, UniformDistribution[{0, 1}], "TestConclusion"]
The null hypothesis that the data is distributed according to the UniformDistribution[{0,1}] is not rejected at the 5. percent level based on the Kolmogorov-Smirnov test.
Here I'd be tempted to say: victory!
Obviously, if you've read until here, either you like what I write (and I'd appreciate an upvote) or you are an expert, in which case I welcome comments on my empirical investigation. Thanks!
SystemDialogInput["RecordSound"]
, suggested by @Searke, doesn't seem to work on OS X. If it did I'd have written up some code to play with the least significant bits of the input. These I would a priori expect to be random, but they could well be otherwise due to all sorts of things-so it would have been fun. Alas... $\endgroup$