paulrd
September 10, 2014, 10:12pm
1
As I understand it, the Arduino's random number generator produces a sequence of pseudorandom numbers that eventually repeats. Giving it a new seed with RandomSeed() doesn't change to a different number sequence, it just jumps to a different location within the same sequence.
My questions are:
How long is this sequence; ie how many numbers can be produced before they start repeating? And is an 8-bit value in RandomSeed() enough to make sure repeated sequences of 'random' numbers are vanishingly unlikely?
Yup. Yup. 2^31-1. Doubt it. Using an eight bit seed will make the quality of the sequence significantly lower. You are far better served saving / restoring the seed so the generator can run through the full cycle.
More information...
Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
paulrd
September 12, 2014, 9:47am
3
Thanks for your advice, that's helpful. How does one access the random seed in Arduino code?
Call random() to read the next value in the sequence which is the new seed value.
Call srandom(value) to set the seed.
This may help...
http://forum.arduino.cc/index.php?topic=66206.0
MarkT
September 13, 2014, 8:54pm
7
The code for random() is alas in stdlib, so no source that I can find, however the
disassembly suggests its just a linear congruence generator, which are usually maximal
sequences over 2^n-1 (but poor quality randomness).
[ quick bit of research suggests from the integer constants in the code that its this
function: http://open-last-chaos.googlecode.com/svn/trunk/Connector/src/Random.cpp
In fact here's the original paper about all this:
]
Oh yes, never use such a pseudo random generator for cryptography, they are not
in any way secure.
See this thread for a discussion about alternatives:
http://forum.arduino.cc/index.php?topic=263849
Example random generator:
/* Implementation of a 32-bit KISS generator which uses no multiply instructions */
static unsigned long x=123456789,y=234567891,z=345678912,w=456789123,c=0;
unsigned long JKISS32()
{
long t;
y ^= y << 5;
y ^= y >> 7;
y ^= y << 22;
t = z + w + c;
z = w;
c = t < 0;
w = t & 2147483647;
x += 1411392427;
return x + y + w;
}
Example seeder:
void tc_RandomSeed_JKISS32( unsigned long newseed )
{
if ( newseed != 0 )
{
x = 123456789;
y = newseed;
z = 345678912;
w = 456789123;
c = 0;
}
}
MarkT
September 13, 2014, 9:25pm
10
/* Implementation of a 32-bit KISS generator which uses no multiply instructions */
Does it pass statistical testsuites?
What is the cycle length?
Are there any short cycles?
Are there bad seed values?
See this: http://zygomorphic.com/arduino-tiny/?p=39
From the PDF linked to on that page ( http://www.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf ):
If your language only handles 32-bit integers or even has difficulties with unsigned integers (e.g.
Fortran), or you just want a faster generator that avoids multiplication, Marsaglia has proposed
using an add-with-carry generator rather than the superior multiply-with-carry. Although I prefer to
use the full multiplicative KISS, I do really like this 32-bit generator because it is the simplest and
fastest RNG that I know of which passes all of the Dieharder tests and the BigCrunch tests in
TestU01. The period is ?2 121 = 2.6 x 10 36 .
I made this test case on Ubuntu (using unsigned int because that is what gives a 32-bit int on this processor):
#include <stdio.h>
/* Implementation of a 32-bit KISS generator which uses no multiply instructions */
static unsigned int x=123456789,y=234567891,z=345678912,w=456789123,c=0;
unsigned int JKISS32()
{
int t;
y ^= y << 5;
y ^= y >> 7;
y ^= y << 22;
t = z + w + c;
z = w;
c = t < 0;
w = t & 2147483647;
x += 1411392427;
return x + y + w;
}
int main ()
{
while (1)
{
unsigned int r = JKISS32 ();
fwrite (&r, 4, 1, stdout);
}
return 0;
}
I tested like this:
./kiss32 | dieharder -a -g 200
Resuts:
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
stdin_input_raw| 5.80e+07 |4078820604|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.03307654| PASSED
diehard_operm5| 0| 1000000| 100|0.88004083| PASSED
diehard_rank_32x32| 0| 40000| 100|0.41930350| PASSED
diehard_rank_6x8| 0| 100000| 100|0.73296855| PASSED
diehard_bitstream| 0| 2097152| 100|0.48591461| PASSED
diehard_opso| 0| 2097152| 100|0.90387282| PASSED
diehard_oqso| 0| 2097152| 100|0.28068373| PASSED
diehard_dna| 0| 2097152| 100|0.83973715| PASSED
diehard_count_1s_str| 0| 256000| 100|0.58849849| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.26356325| PASSED
diehard_parking_lot| 0| 12000| 100|0.37131003| PASSED
diehard_2dsphere| 2| 8000| 100|0.70442965| PASSED
diehard_3dsphere| 3| 4000| 100|0.34122464| PASSED
diehard_squeeze| 0| 100000| 100|0.08921174| PASSED
diehard_sums| 0| 100| 100|0.08147020| PASSED
diehard_runs| 0| 100000| 100|0.08217975| PASSED
diehard_runs| 0| 100000| 100|0.55030731| PASSED
diehard_craps| 0| 200000| 100|0.36079634| PASSED
diehard_craps| 0| 200000| 100|0.76707625| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.79448111| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.14836390| PASSED
sts_monobit| 1| 100000| 100|0.98497349| PASSED
sts_runs| 2| 100000| 100|0.86910149| PASSED
sts_serial| 1| 100000| 100|0.11369182| PASSED
sts_serial| 2| 100000| 100|0.89986352| PASSED
sts_serial| 3| 100000| 100|0.98999508| PASSED
sts_serial| 3| 100000| 100|0.29880385| PASSED
sts_serial| 4| 100000| 100|0.84496312| PASSED
sts_serial| 4| 100000| 100|0.51021114| PASSED
sts_serial| 5| 100000| 100|0.27616427| PASSED
sts_serial| 5| 100000| 100|0.12839023| PASSED
sts_serial| 6| 100000| 100|0.16757307| PASSED
sts_serial| 6| 100000| 100|0.84914965| PASSED
sts_serial| 7| 100000| 100|0.79000677| PASSED
sts_serial| 7| 100000| 100|0.34011136| PASSED
sts_serial| 8| 100000| 100|0.59161490| PASSED
sts_serial| 8| 100000| 100|0.19817940| PASSED
sts_serial| 9| 100000| 100|0.19569430| PASSED
sts_serial| 9| 100000| 100|0.06356432| PASSED
sts_serial| 10| 100000| 100|0.35587449| PASSED
sts_serial| 10| 100000| 100|0.43895831| PASSED
sts_serial| 11| 100000| 100|0.38808666| PASSED
sts_serial| 11| 100000| 100|0.02427212| PASSED
sts_serial| 12| 100000| 100|0.13959434| PASSED
sts_serial| 12| 100000| 100|0.87174304| PASSED
sts_serial| 13| 100000| 100|0.44789999| PASSED
sts_serial| 13| 100000| 100|0.90732514| PASSED
sts_serial| 14| 100000| 100|0.82787900| PASSED
sts_serial| 14| 100000| 100|0.56555426| PASSED
sts_serial| 15| 100000| 100|0.89826206| PASSED
sts_serial| 15| 100000| 100|0.56477897| PASSED
sts_serial| 16| 100000| 100|0.48666295| PASSED
sts_serial| 16| 100000| 100|0.47952162| PASSED
rgb_bitdist| 1| 100000| 100|0.47766161| PASSED
rgb_bitdist| 2| 100000| 100|0.72380052| PASSED
rgb_bitdist| 3| 100000| 100|0.25048127| PASSED
rgb_bitdist| 4| 100000| 100|0.10199211| PASSED
rgb_bitdist| 5| 100000| 100|0.71996662| PASSED
rgb_bitdist| 6| 100000| 100|0.86952107| PASSED
rgb_bitdist| 7| 100000| 100|0.59202528| PASSED
rgb_bitdist| 8| 100000| 100|0.81732167| PASSED
rgb_bitdist| 9| 100000| 100|0.44798263| PASSED
rgb_bitdist| 10| 100000| 100|0.57945709| PASSED
rgb_bitdist| 11| 100000| 100|0.16639436| PASSED
rgb_bitdist| 12| 100000| 100|0.43479261| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.00133214| WEAK
rgb_minimum_distance| 3| 10000| 1000|0.74242368| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.35832274| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.93948454| PASSED
rgb_permutations| 2| 100000| 100|0.19907388| PASSED
rgb_permutations| 3| 100000| 100|0.54692330| PASSED
rgb_permutations| 4| 100000| 100|0.96139064| PASSED
rgb_permutations| 5| 100000| 100|0.39983634| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.30933462| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.55797078| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.90683136| PASSED
rgb_lagged_sum| 3| 1000000| 100|0.40325839| PASSED
rgb_lagged_sum| 4| 1000000| 100|0.01898008| PASSED
rgb_lagged_sum| 5| 1000000| 100|0.28246826| PASSED
rgb_lagged_sum| 6| 1000000| 100|0.97510313| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.79719067| PASSED
... (some tests omitted due to message length, all passed) ...
rgb_lagged_sum| 25| 1000000| 100|0.96320195| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.25663265| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.77025868| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.64147414| PASSED
rgb_lagged_sum| 29| 1000000| 100|0.09946415| PASSED
rgb_lagged_sum| 30| 1000000| 100|0.48749316| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.56088593| PASSED
rgb_lagged_sum| 32| 1000000| 100|0.46998944| PASSED
rgb_kstest_test| 0| 10000| 1000|0.24784991| PASSED
dab_bytedistrib| 0| 51200000| 1|0.11155315| PASSED
dab_dct| 256| 50000| 1|0.19169188| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.62717317| PASSED
dab_filltree| 32| 15000000| 1|0.06917141| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.26124064| PASSED
dab_filltree2| 1| 5000000| 1|0.01206766| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|0.80531060| PASSED
MarkT:
Are there bad seed values?
y = 0 is probably a bad choice.
Bear in mind the seed function above is incomplete. What I remember is that all five variables are the seed and should be initialized. I wrote the function above to make the generator more compatible with the existing Arduino API.
Compare to the Marsenne Twiser output:
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
mt19937_1998| 1.96e+08 |4002501982|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.61671435| PASSED
diehard_operm5| 0| 1000000| 100|0.78296503| PASSED
diehard_rank_32x32| 0| 40000| 100|0.72498389| PASSED
diehard_rank_6x8| 0| 100000| 100|0.99696732| WEAK
diehard_bitstream| 0| 2097152| 100|0.14592345| PASSED
diehard_opso| 0| 2097152| 100|0.73904667| PASSED
diehard_oqso| 0| 2097152| 100|0.16070148| PASSED
diehard_dna| 0| 2097152| 100|0.40260265| PASSED
diehard_count_1s_str| 0| 256000| 100|0.78313093| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.55209698| PASSED
diehard_parking_lot| 0| 12000| 100|0.68281092| PASSED
diehard_2dsphere| 2| 8000| 100|0.59812934| PASSED
diehard_3dsphere| 3| 4000| 100|0.34017222| PASSED
diehard_squeeze| 0| 100000| 100|0.25804905| PASSED
diehard_sums| 0| 100| 100|0.13664518| PASSED
diehard_runs| 0| 100000| 100|0.26409703| PASSED
diehard_runs| 0| 100000| 100|0.20515133| PASSED
diehard_craps| 0| 200000| 100|0.35732336| PASSED
diehard_craps| 0| 200000| 100|0.73578965| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.50002373| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.01039527| PASSED
sts_monobit| 1| 100000| 100|0.98469816| PASSED
sts_runs| 2| 100000| 100|0.01522842| PASSED
sts_serial| 1| 100000| 100|0.21567355| PASSED
sts_serial| 2| 100000| 100|0.88353218| PASSED
sts_serial| 3| 100000| 100|0.95415130| PASSED
sts_serial| 3| 100000| 100|0.83095013| PASSED
sts_serial| 4| 100000| 100|0.15906879| PASSED
sts_serial| 4| 100000| 100|0.23125226| PASSED
sts_serial| 5| 100000| 100|0.30323405| PASSED
sts_serial| 5| 100000| 100|0.40194244| PASSED
sts_serial| 6| 100000| 100|0.79762014| PASSED
sts_serial| 6| 100000| 100|0.94139973| PASSED
sts_serial| 7| 100000| 100|0.45821083| PASSED
sts_serial| 7| 100000| 100|0.13571559| PASSED
sts_serial| 8| 100000| 100|0.84743195| PASSED
sts_serial| 8| 100000| 100|0.89917131| PASSED
sts_serial| 9| 100000| 100|0.43459363| PASSED
sts_serial| 9| 100000| 100|0.23222254| PASSED
sts_serial| 10| 100000| 100|0.62195134| PASSED
sts_serial| 10| 100000| 100|0.44435275| PASSED
sts_serial| 11| 100000| 100|0.71449679| PASSED
sts_serial| 11| 100000| 100|0.57682450| PASSED
sts_serial| 12| 100000| 100|0.94901857| PASSED
sts_serial| 12| 100000| 100|0.63005802| PASSED
sts_serial| 13| 100000| 100|0.62593073| PASSED
sts_serial| 13| 100000| 100|0.46734226| PASSED
sts_serial| 14| 100000| 100|0.95773048| PASSED
sts_serial| 14| 100000| 100|0.25408097| PASSED
sts_serial| 15| 100000| 100|0.62858344| PASSED
sts_serial| 15| 100000| 100|0.33350146| PASSED
sts_serial| 16| 100000| 100|0.89530940| PASSED
sts_serial| 16| 100000| 100|0.07069205| PASSED
rgb_bitdist| 1| 100000| 100|0.32359881| PASSED
rgb_bitdist| 2| 100000| 100|0.87421156| PASSED
rgb_bitdist| 3| 100000| 100|0.90785453| PASSED
rgb_bitdist| 4| 100000| 100|0.85770089| PASSED
rgb_bitdist| 5| 100000| 100|0.62290857| PASSED
rgb_bitdist| 6| 100000| 100|0.92161877| PASSED
rgb_bitdist| 7| 100000| 100|0.73153391| PASSED
rgb_bitdist| 8| 100000| 100|0.56943049| PASSED
rgb_bitdist| 9| 100000| 100|0.47165819| PASSED
rgb_bitdist| 10| 100000| 100|0.56769214| PASSED
rgb_bitdist| 11| 100000| 100|0.98817277| PASSED
rgb_bitdist| 12| 100000| 100|0.60181406| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.90157061| PASSED
rgb_minimum_distance| 3| 10000| 1000|0.46061896| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.82815090| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.07133976| PASSED
rgb_permutations| 2| 100000| 100|0.32011924| PASSED
rgb_permutations| 3| 100000| 100|0.29167639| PASSED
rgb_permutations| 4| 100000| 100|0.79678442| PASSED
rgb_permutations| 5| 100000| 100|0.27139662| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.24347131| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.69879574| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.99516796| WEAK
rgb_lagged_sum| 3| 1000000| 100|0.91469411| PASSED
rgb_lagged_sum| 4| 1000000| 100|0.09091343| PASSED
rgb_lagged_sum| 5| 1000000| 100|0.90032653| PASSED
rgb_lagged_sum| 6| 1000000| 100|0.32236123| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.17326903| PASSED
rgb_lagged_sum| 8| 1000000| 100|0.40710864| PASSED
rgb_lagged_sum| 9| 1000000| 100|0.39178375| PASSED
rgb_lagged_sum| 10| 1000000| 100|0.14601055| PASSED
rgb_lagged_sum| 11| 1000000| 100|0.57822219| PASSED
rgb_lagged_sum| 12| 1000000| 100|0.99709220| WEAK
rgb_lagged_sum| 13| 1000000| 100|0.66993278| PASSED
rgb_lagged_sum| 14| 1000000| 100|0.32737613| PASSED
rgb_lagged_sum| 15| 1000000| 100|0.62091065| PASSED
rgb_lagged_sum| 16| 1000000| 100|0.91199984| PASSED
rgb_lagged_sum| 17| 1000000| 100|0.09579321| PASSED
rgb_lagged_sum| 18| 1000000| 100|0.88220512| PASSED
rgb_lagged_sum| 19| 1000000| 100|0.62465972| PASSED
rgb_lagged_sum| 20| 1000000| 100|0.99458705| PASSED
rgb_lagged_sum| 21| 1000000| 100|0.41738784| PASSED
rgb_lagged_sum| 22| 1000000| 100|0.08671640| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.46819785| PASSED
rgb_lagged_sum| 24| 1000000| 100|0.79054363| PASSED
rgb_lagged_sum| 25| 1000000| 100|0.90831002| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.84671412| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.87013638| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.40800397| PASSED
rgb_lagged_sum| 29| 1000000| 100|0.69498499| PASSED
rgb_lagged_sum| 30| 1000000| 100|0.38559746| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.50844188| PASSED
rgb_lagged_sum| 32| 1000000| 100|0.78982750| PASSED
rgb_kstest_test| 0| 10000| 1000|0.36267308| PASSED
dab_bytedistrib| 0| 51200000| 1|0.17295188| PASSED
dab_dct| 256| 50000| 1|0.47664115| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.11714740| PASSED
dab_filltree| 32| 15000000| 1|0.98918917| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.54267305| PASSED
dab_filltree2| 1| 5000000| 1|0.70933128| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|0.76447055| PASSED
In regards to dieharder and other statistical tests: sometimes p happens. An occasional "weak" is normal. A rare "fail" is even normal.
In other words, they are both very good generators.
Just for fun I tested with y = 0; and got this (first few tests):
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
stdin_input_raw| 5.77e+07 |3379353257|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.30075036| PASSED
diehard_operm5| 0| 1000000| 100|0.00000000| FAILED
diehard_rank_32x32| 0| 40000| 100|0.79829420| PASSED
diehard_rank_6x8| 0| 100000| 100|0.00000000| FAILED
diehard_bitstream| 0| 2097152| 100|0.95327567| PASSED
diehard_opso| 0| 2097152| 100|0.04170381| PASSED
diehard_oqso| 0| 2097152| 100|0.00000000| FAILED
diehard_dna| 0| 2097152| 100|0.00000000| FAILED
diehard_count_1s_str| 0| 256000| 100|0.00000013| FAILED
diehard_count_1s_byt| 0| 256000| 100|0.00000000| FAILED
diehard_parking_lot| 0| 12000| 100|0.00000000| FAILED
diehard_2dsphere| 2| 8000| 100|0.08659795| PASSED
diehard_3dsphere| 3| 4000| 100|0.00000000| FAILED
diehard_squeeze| 0| 100000| 100|0.00000000| FAILED
diehard_sums| 0| 100| 100|0.00000000| FAILED
diehard_runs| 0| 100000| 100|0.00000000| FAILED
diehard_runs| 0| 100000| 100|0.00000000| FAILED
diehard_craps| 0| 200000| 100|0.00000000| FAILED
diehard_craps| 0| 200000| 100|0.00000000| FAILED
marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED
marsaglia_tsang_gcd| 0| 10000000| 100|0.86801918| PASSED
sts_monobit| 1| 100000| 100|0.85904846| PASSED
But with y = 1, I got this:
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
stdin_input_raw| 5.91e+07 |3198615326|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.76352514| PASSED
diehard_operm5| 0| 1000000| 100|0.80969575| PASSED
diehard_rank_32x32| 0| 40000| 100|0.98443640| PASSED
diehard_rank_6x8| 0| 100000| 100|0.79245553| PASSED
diehard_bitstream| 0| 2097152| 100|0.15452550| PASSED
diehard_opso| 0| 2097152| 100|0.51475611| PASSED
diehard_oqso| 0| 2097152| 100|0.48991786| PASSED
diehard_dna| 0| 2097152| 100|0.35247335| PASSED
diehard_count_1s_str| 0| 256000| 100|0.45942448| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.78867631| PASSED
diehard_parking_lot| 0| 12000| 100|0.39654442| PASSED
diehard_2dsphere| 2| 8000| 100|0.97480966| PASSED
diehard_3dsphere| 3| 4000| 100|0.99392773| PASSED
diehard_squeeze| 0| 100000| 100|0.11012652| PASSED
diehard_sums| 0| 100| 100|0.59669025| PASSED
diehard_runs| 0| 100000| 100|0.96140155| PASSED
diehard_runs| 0| 100000| 100|0.30936843| PASSED
diehard_craps| 0| 200000| 100|0.62925693| PASSED
diehard_craps| 0| 200000| 100|0.91845410| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.95353259| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.73556725| PASSED
sts_monobit| 1| 100000| 100|0.87830484| PASSED
Yes, if they always passed they would be too predicatable, eh?
Pseudorandom number generators are perfectly predictable so predictability is not the issue.
This is the issue...
I'll use flipping a coin as an example. If I flip a coin one million times and get heads one million times, is the coin fair? It turns out, we can't tell. The chances of one million heads from one million flips is extremely low. But the chances are not zero. That's the important part. The chances are not zero. With truly random numbers, every so often something freaky happens. When a sequence fails a single statistical test, we cannot tell if the failure is because the sequence is low quality or because something freaky happened. "Sometimes p happens" means that even truly random numbers occasionally fail a p-test.
Note: In general the above is true. Some of the tests are designed to test for specific kinds of PRNG failures. I suspect searching for RANDU will uncover a good example. I believe the Libc random function always fails diehard_rank_32x32 and diehard_rank_6x8.
I was making a bit of a joke. Say we tossed a coin 100 times and always, every time, got 50 heads and 50 tails, there would be something wrong. Why? Because after 99 tosses we know what the final one must be. Therefore it's not random, even if the results inbetween "look random".
Interestingly, I believe (some) games actually filter the random number output to make it "look random".
Say, for example, after doing a number of dungeons, at the end you and I "roll" for the single piece of good loot. And say I won it twice in a row (which is certainly possible with random numbers). If I won it a third time in a row you would probably complain to the game manufacturers that their random number generator is faulty. So they take care that the same number doesn't come up too often in a row, even if that defeats true randomness.
Example reference:
Reseeding won't help jSepia's concern, which is really about runs rather than repetition. Pure randomness does lead to unfun gameplay, so it's a real concern and plenty of games have addressed it.
Also:
One thing you may want to look into is a Streak Breaker for continued series of randoms. If your game has a “chance to hit” variable on combat, putting a Streak Breaker in can take some of the sting out of missing in a long series of misses by ensuring the next attack is autohit.
Sorry about that. I'm a bit distracted today. Swapped my 15 year old truck for one with ... 3.4 miles! Got pickup on the brain.