verification of randomness
-----BEGIN PGP SIGNED MESSAGE----- Hi... I've built a random number generator based on the noise of a Zener diode. Now I'd like to verify it's correct operation. I'd be very grateful if someone could point me to existing software for randomness tests or additional tests not mentioned in Knuth. I'll make the design of the generator available as soon as I've verified it's operation. Andreas -----BEGIN PGP SIGNATURE----- Version: 2.6.2 Comment: Processed by Mailcrypt 3.4, an Emacs/PGP interface iQCVAgUBMRVyMUyjTSyISdw9AQHUSQP/UK3ubued8U3iB4FDO5WAsiEV+F2/100O 0w42NSZbry5+07u+l9eJN/ogpECZ9yIltWM7slkKZS0q0TGQ4zCucHoDPKhubMHs gQqjkmgXTs0drqRn+BYPoQFYPyiYLeBr67BRqsQFyp7neuMC5NN10NpL9y4bcAS2 8NBB7yFh9d0= =RD04 -----END PGP SIGNATURE-----
andreas@artcom.de (Andreas Bogk) writes:
I've built a random number generator based on the noise of a Zener diode. Now I'd like to verify it's correct operation. I'd be very grateful if someone could point me to existing software for randomness tests or additional tests not mentioned in Knuth.
Dear Andreas, Here are a couple of tests: 1. Maurer's test (very good, published later than Knuth) /************************************************************ Ueli Maurer's randomness test (C) 1993 Dimitri Vulis, all rights reserved For details, see: Ueli M. Maurer. ``A Universal Statistical Test for Random Bit Generators.'' {\em Journal of Cryptology,\/} {\bf5} (1992), pp.~89--105. *************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> void rndinit(void); unsigned char rndgetbyte(void); /* We produce a stream of random bits. We look at them in blocks of L bits at a time. Maurer uses 8-bit bytes s_n, recommends 6 <= L <= 16. */ #define L 8 /* 2**L */ #define vv 256 int main(void) { /* the count of s_n's random bytes */ long n=1; /* fTU is the average \log_2(a_n), where a_n is the number of bytes since the previous occurrence of the same value. a_n = n for first occurrence (hopefully skipped using Q below) */ double fTU=0.0; /* Every time we obtain a random byte, we save its position n here, so we can computer a_n, the number of bytes since last occurence */ static long lastseen[vv]; /* the number of bytes to skip before computing compute the average, hoping that all possible byte values will occur and lastseen will be non-zero. M recommends Q >= 10 * 2**L. */ long Q; /* the number of bytes to use to compute fTU. M recommends K as large as possible >= 1000 * 2**L */ long K; /* E(L) is the expected value of fTU for a truly random sequence */ #define E 7.1836656 /* V(L) is the variance of a_n for a truly random sequence (from M; table below) */ #define V 3.238 /* If you decide to change L: L E V 6 5.2177052 2.954 7 6.1962507 3.125 8 7.1836656 3.238 9 8.1764248 3.311 10 9.1723243 3.356 11 10.170032 3.384 12 11.168765 3.401 13 12.168070 3.410 14 13.167693 3.416 15 14.167488 3.419 16 15.167379 3.421 */ /* c(L,K) from M (13) */ double C; /* standard deviation of a truly random sequence from M (14) */ double sigma; /* fTU's distance in sigmas from the expected value */ double y; /* rho is the rejection rate, the probability that a sequence is bad */ double rho; unsigned char r; printf("Enter Q>=%-7ld:",10L*vv); fflush(stdout); scanf("%ld",&Q); printf("Enter K>=%-7ld:",1000L*vv); fflush(stdout); scanf("%ld",&K); C=0.7-0.8/L+(1.6+12.8/L)*pow(K,-4.0/L); /* (13) */ /* M: C close to 0.6 for L=8 */ sigma=C*pow(V/K,0.5); /* (14) */ /* M: grows as 1/\sqrt{K} */ /* initialize lastseen to 0 */ memset((void*)lastseen,0,sizeof(lastseen)); rndinit(); for (; n<Q+K; n++) { r=rndgetbyte(); if (n>Q) fTU+=log((double)(n-lastseen[r])); lastseen[r]=n; } /* compute the average and convert from natural log to log_2 */ fTU/=K*log(2.0); y=fabs((fTU-E)/sigma); rho=erf(-y/sqrt(2.0))+1; printf("fTU=%lg, %lg*%lg from e.v. %lg, rho=%lg --- %s\n", fTU,y,sigma,E,rho, (rho < 0.0001 ? "unacceptable" : (rho < 0.001 ? "marginal" : "acceptable"))); return(0); } /* Use C library's pseudo-random number generator */ void rndinit(void) { srand(1); } unsigned char rndgetbyte(void) { static unsigned state=0; static int rrr; if (state^=1) { rrr=rand(); return(unsigned char)(rrr & 0xff); } else return(unsigned char)((rrr>>8) & 0xff); } 2. You probably have this one: /* *************************************************************** * chi.c -- * * Copyright 1993 Peter K. Boucher * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. * * Usage: chi [input_file [output_file]] * * This program counts the occurances of each character in a file * and notifies the user when a the distribution is too ragged or * too uniform. * * Because the chance of getting byte B after byte A should be 1:256 * (for all A's and B's), the program also checks that the successors * to each byte are randomly distributed. This means that for each byte * value (0 - 255) that occurs in the text, a count is kept of the * byte value that followed in the text, and the frequency distribution * of these succeeding bytes is also checked. * */ #include <stdio.h> #define NUM_BYTES 256L #define BUFSIZE 8192 #define min_nps 5.0 #define min_testable (NUM_BYTES*min_nps) #define V01 (205.33) /* 1% chance it's less */ #define V05 (219.09) /* 5% chance it's less */ #define V25 (239.39) /* 25% chance it's less */ #define V50 (254.33) /* 50% chance it's less */ #define V75 (269.88) /* 75% chance it's less */ #define V95 (293.16) /* 95% chance it's less */ #define V99 (310.57) /* 99% chance it's less */ #define min_chichi5 (20.0*min_nps) /* min prob. 5% */ #define min_chichi3 (4.0*min_nps) /* min prob. 25% */ #ifdef DEBUG #define CFNAME "chi.dat" #define min_chichi7 (100.0*min_nps) /* min prob. 1% */ #endif #define AB(X) (((X) >= 0.0) ? (X) : -(X)) double cnt[NUM_BYTES] = {0.0}; /* should be all zeros. */ double successors[NUM_BYTES][NUM_BYTES] = {{0.0}}; /* should be all zeros. */ static unsigned char buf[BUFSIZE]; static FILE *ifp, *ofp; FILE * my_fopen(file, type) char *file, *type; { FILE *fp; if ((fp = fopen(file, type)) == NULL) { (void)fprintf(stderr, "Can't open '%s' for '%s'\n", file, type); exit(1); } return(fp); } double get_V(n,Y) double n; double *Y; { #define k (256) #define p (1.0/256.0) double sum = 0.0; double divider = (n*p); double tmp; long i; for (i=0; i<k; i++) { tmp = Y[i]/divider; sum += (tmp*Y[i]); } return( sum - n ); } double chichi3(n,cgt_75,c50_75,c25_50,clt_25) double n,cgt_75,c50_75,c25_50,clt_25; { double sum = (cgt_75*cgt_75)/(0.25*n); sum += (c50_75*c50_75)/(0.25*n); sum += (c25_50*c25_50)/(0.25*n); sum += (clt_25*clt_25)/(0.25*n); return( sum - n ); } int check_chichi3(n,cgt_75,c50_75,c25_50,clt_25) double n,cgt_75,c50_75,c25_50,clt_25; { #define C3_01 0.1148 #define C3_05 0.3518 #define C3_25 1.213 #define C3_75 4.108 #define C3_95 7.815 #define C3_99 11.34 double V3; int check = 0; if (n < min_chichi3) { return( 0 ); } if ((V3 = chichi3(n,cgt_75,c50_75,c25_50,clt_25)) > C3_75) { check++; if (V3 > C3_95) { check++; if (V3 > C3_99) { check++; } } } else if (V3 < C3_25) { check--; if (V3 < C3_05) { check--; if (V3 < C3_01) { check--; } } } return(check); } double chichi5(n,cgt_95,c75_95,c50_75,c25_50,c05_25,clt_05) double n,cgt_95,c75_95,c50_75,c25_50,c05_25,clt_05; { double sum = (cgt_95*cgt_95)/(0.05*n); sum += (c75_95*c75_95)/(0.20*n); sum += (c50_75*c50_75)/(0.25*n); sum += (c25_50*c25_50)/(0.25*n); sum += (c05_25*c05_25)/(0.20*n); sum += (clt_05*clt_05)/(0.05*n); return( sum - n ); } int check_chichi5(n,cgt_95,c75_95,c50_75,c25_50,c05_25,clt_05) double n,cgt_95,c75_95,c50_75,c25_50,c05_25,clt_05; { #define C5_01 0.5543 #define C5_05 1.1455 #define C5_25 2.675 #define C5_75 6.626 #define C5_95 11.07 #define C5_99 15.09 double V5; int check = 0; if (n < min_chichi5) { return( check_chichi3(n,cgt_95+c75_95,c50_75,c25_50,c05_25+clt_05) ); } if ((V5 = chichi5(n,cgt_95,c75_95,c50_75,c25_50,c05_25,clt_05)) > C5_75) { check++; if (V5 > C5_95) { check++; if (V5 > C5_99) { check++; } } } else if (V5 < C5_25) { check--; if (V5 < C5_05) { check--; if (V5 < C5_01) { check--; } } } return(check); } #ifdef DEBUG double chichi7(n,cgt_99,c95_99,c75_95,c50_75,c25_50,c05_25,c01_05,clt_01) double n,cgt_99,c95_99,c75_95,c50_75,c25_50,c05_25,c01_05,clt_01; { double sum = (cgt_99*cgt_99)/(0.01*n); sum += (c95_99*c95_99)/(0.04*n); sum += (c75_95*c75_95)/(0.20*n); sum += (c50_75*c50_75)/(0.25*n); sum += (c25_50*c25_50)/(0.25*n); sum += (c05_25*c05_25)/(0.20*n); sum += (c01_05*c01_05)/(0.04*n); sum += (clt_01*clt_01)/(0.01*n); return( sum - n ); } int check_chichi7(n,cgt_99,c95_99,c75_95,c50_75,c25_50,c05_25,c01_05,clt_01) double n,cgt_99,c95_99,c75_95,c50_75,c25_50,c05_25,c01_05,clt_01; { #define C7_01 1.239 #define C7_05 2.167 #define C7_25 4.255 #define C7_75 9.037 #define C7_95 14.07 #define C7_99 18.48 double V7; int check = 0; if (n < min_chichi7) { return( check_chichi5(n,cgt_99+c95_99,c75_95,c50_75, c25_50,c05_25,c01_05+clt_01) ); } if ((V7=chichi7(n,cgt_99,c95_99,c75_95,c50_75, c25_50,c05_25,c01_05,clt_01)) > C7_75) { check++; if (V7 > C7_95) { check++; if (V7 > C7_99) { check++; } } } else if (V7 < C7_25) { check--; if (V7 < C7_05) { check--; if (V7 < C7_01) { check--; } } } return(check); } #endif double fill_arrays() { double size=0.0; long ch,next,l,i; if ((ch = getc(ifp)) != EOF) { /* prime the pump */ cnt[ch] = size = 1.0; while ((l = fread(buf, 1, BUFSIZE, ifp)) > 0) { for (i=0; i<l; i++) { size++; next = buf[i]; cnt[next]++; successors[ch][next]++; ch = next; } } } fclose(ifp); return( size ); } void chi_2_test() { long i; double suc_tests = 0.0; double suc_highest = -1.0; double suc_lowest = 1000000000.0; double suc_gt_99 = 0.0; double suc_95_99 = 0.0; double suc_75_95 = 0.0; double suc_50_75 = 0.0; double suc_25_50 = 0.0; double suc_05_25 = 0.0; double suc_01_05 = 0.0; double suc_lt_01 = 0.0; double V; double size; char *desc = NULL; #ifdef DEBUG double tocc_tests = 0.0; double tocc_highest = -1.0; double tocc_lowest = 1000000000.0; double tocc_gt_99 = 0.0; double tocc_95_99 = 0.0; double tocc_75_95 = 0.0; double tocc_50_75 = 0.0; double tocc_25_50 = 0.0; double tocc_05_25 = 0.0; double tocc_01_05 = 0.0; double tocc_lt_01 = 0.0; double tsuc_tests = 0.0; double tsuc_highest = -1.0; double tsuc_lowest = 1000000000.0; double tsuc_gt_99 = 0.0; double tsuc_95_99 = 0.0; double tsuc_75_95 = 0.0; double tsuc_50_75 = 0.0; double tsuc_25_50 = 0.0; double tsuc_05_25 = 0.0; double tsuc_01_05 = 0.0; double tsuc_lt_01 = 0.0; FILE *chi_dat=fopen(CFNAME, "r"); #endif if ((size = fill_arrays()) < min_testable) { fprintf(ofp, "File too small (%.0f) to meaningfully analyze\n", size); exit(0); } #ifdef DEBUG if (chi_dat != NULL) { char dummy[128]; fscanf(chi_dat, "%lf", &tocc_tests); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_highest); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_lowest); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_gt_99); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_95_99); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_75_95); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_50_75); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_25_50); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_05_25); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_01_05); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tocc_lt_01); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_tests); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_highest); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_lowest); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_gt_99); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_95_99); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_75_95); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_50_75); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_25_50); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_05_25); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_01_05); fgets(dummy, 128, chi_dat); fscanf(chi_dat, "%lf", &tsuc_lt_01); fgets(dummy, 128, chi_dat); fclose(chi_dat); } #endif if ((V = get_V(size,cnt)) > V99) { desc = ": *******Non-random (hi)\n"; #ifdef DEBUG tocc_gt_99++; #endif } else if (V > V95) { desc = ": Suspect (hi)\n"; #ifdef DEBUG tocc_95_99++; #endif } else if (V > V75) { desc = ": Acceptible (hi)\n"; #ifdef DEBUG tocc_75_95++; #endif } else if (V > V50) { desc = ": Excellent (hi) !!!!!!!\n"; #ifdef DEBUG tocc_50_75++; #endif } else if (V > V25) { desc = ": Excellent (lo) !!!!!!!\n"; #ifdef DEBUG tocc_25_50++; #endif } else if (V > V05) { desc = ": Acceptible (lo)\n"; #ifdef DEBUG tocc_05_25++; #endif } else if (V > V01) { desc = ": Suspect (lo)\n"; #ifdef DEBUG tocc_01_05++; #endif } else { desc = ": *******Non-random (lo)\n"; #ifdef DEBUG tocc_lt_01++; #endif } fprintf(ofp, "Occurance V = %.2f (n = %.0f)%s", V, size, desc); #ifdef DEBUG tocc_tests++; if (V < tocc_lowest) tocc_lowest = V; if (V > tocc_highest) tocc_highest = V; #endif for (i=0; i<NUM_BYTES; i++) { if (cnt[i] >= min_testable) { if ((V = get_V(cnt[i],successors[i])) > V99) { suc_gt_99++; } else if (V > V95) { suc_95_99++; } else if (V > V75) { suc_75_95++; } else if (V > V50) { suc_50_75++; } else if (V > V25) { suc_25_50++; } else if (V > V05) { suc_05_25++; } else if (V > V01) { suc_01_05++; } else { suc_lt_01++; } suc_tests++; if (V < suc_lowest) suc_lowest = V; if (V > suc_highest) suc_highest = V; } } if (suc_tests > 0.0) { fprintf(ofp, "Successor Vd = %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n", suc_gt_99*100.0/suc_tests, suc_95_99*100.0/suc_tests, suc_75_95*100.0/suc_tests, suc_50_75*100.0/suc_tests, suc_25_50*100.0/suc_tests, suc_05_25*100.0/suc_tests, suc_01_05*100.0/suc_tests, suc_lt_01*100.0/suc_tests); fprintf(ofp, " deviation %d, (lowest = %.2f, highest = %.2f)\n", check_chichi5(suc_tests, suc_gt_99+suc_95_99,suc_75_95,suc_50_75, suc_25_50,suc_05_25,suc_01_05+suc_lt_01), suc_lowest, suc_highest); } #ifdef DEBUG tsuc_tests += suc_tests; if (suc_lowest < tsuc_lowest) tsuc_lowest = suc_lowest; if (suc_highest > tsuc_highest) tsuc_highest = suc_highest; tsuc_gt_99 += suc_gt_99; tsuc_95_99 += suc_95_99; tsuc_75_95 += suc_75_95; tsuc_50_75 += suc_50_75; tsuc_25_50 += suc_25_50; tsuc_05_25 += suc_05_25; tsuc_01_05 += suc_01_05; tsuc_lt_01 += suc_lt_01; chi_dat = my_fopen(CFNAME, "w"); fprintf(chi_dat, "%-14.0f - Total number of occurance tests\n", tocc_tests); fprintf(chi_dat, "%-14.2f - Highest V from an occurance test\n", tocc_highest); fprintf(chi_dat, "%-14.2f - Lowest V from an occurance test\n", tocc_lowest); fprintf(chi_dat, "%-14.0f - Number of occurance tests above %.2f\n", tocc_gt_99, V99); fprintf(chi_dat, "%-14.0f - Number of occurance tests %.2f - %.2f\n", tocc_95_99, V95, V99); fprintf(chi_dat, "%-14.0f - Number of occurance tests %.2f - %.2f\n", tocc_75_95, V75, V95); fprintf(chi_dat, "%-14.0f - Number of occurance tests %.2f - %.2f\n", tocc_50_75, V50, V75); fprintf(chi_dat, "%-14.0f - Number of occurance tests %.2f - %.2f\n", tocc_25_50, V25, V50); fprintf(chi_dat, "%-14.0f - Number of occurance tests %.2f - %.2f\n", tocc_05_25, V05, V25); fprintf(chi_dat, "%-14.0f - Number of occurance tests %.2f - %.2f\n", tocc_01_05, V01, V05); fprintf(chi_dat, "%-14.0f - Number of occurance tests below %.2f\n", tocc_lt_01, V01); fprintf(chi_dat, "%-14.0f - Total number of successor tests\n", tsuc_tests); fprintf(chi_dat, "%-14.2f - Highest V from an successor test\n", tsuc_highest); fprintf(chi_dat, "%-14.2f - Lowest V from an successor test\n", tsuc_lowest); fprintf(chi_dat, "%-14.0f - Number of successor tests above %.2f\n", tsuc_gt_99, V99); fprintf(chi_dat, "%-14.0f - Number of successor tests %.2f - %.2f\n", tsuc_95_99, V95, V99); fprintf(chi_dat, "%-14.0f - Number of successor tests %.2f - %.2f\n", tsuc_75_95, V75, V95); fprintf(chi_dat, "%-14.0f - Number of successor tests %.2f - %.2f\n", tsuc_50_75, V50, V75); fprintf(chi_dat, "%-14.0f - Number of successor tests %.2f - %.2f\n", tsuc_25_50, V25, V50); fprintf(chi_dat, "%-14.0f - Number of successor tests %.2f - %.2f\n", tsuc_05_25, V05, V25); fprintf(chi_dat, "%-14.0f - Number of successor tests %.2f - %.2f\n", tsuc_01_05, V01, V05); fprintf(chi_dat, "%-14.0f - Number of successor tests below %.2f\n", tsuc_lt_01, V01); fprintf(chi_dat, "Occurance Vd = %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f", tocc_gt_99*100.0/tocc_tests, tocc_95_99*100.0/tocc_tests, tocc_75_95*100.0/tocc_tests, tocc_50_75*100.0/tocc_tests, tocc_25_50*100.0/tocc_tests, tocc_05_25*100.0/tocc_tests, tocc_01_05*100.0/tocc_tests, tocc_lt_01*100.0/tocc_tests); fprintf(chi_dat, " (deviation %d)\n", check_chichi7(tocc_tests, tocc_gt_99,tocc_95_99,tocc_75_95,tocc_50_75, tocc_25_50,tocc_05_25,tocc_01_05,tocc_lt_01)); if (tsuc_tests > 0.0) { fprintf(chi_dat, "Successor Vd = %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f", tsuc_gt_99*100.0/tsuc_tests, tsuc_95_99*100.0/tsuc_tests, tsuc_75_95*100.0/tsuc_tests, tsuc_50_75*100.0/tsuc_tests, tsuc_25_50*100.0/tsuc_tests, tsuc_05_25*100.0/tsuc_tests, tsuc_01_05*100.0/tsuc_tests, tsuc_lt_01*100.0/tsuc_tests); fprintf(chi_dat, " (deviation %d)\n", check_chichi7(tsuc_tests, tsuc_gt_99,tsuc_95_99,tsuc_75_95,tsuc_50_75, tsuc_25_50,tsuc_05_25,tsuc_01_05,tsuc_lt_01)); } fclose(chi_dat); #endif } int main(argc,argv) int argc; char **argv; { ifp = (argc > 1) ? my_fopen(argv[1],"rb") : stdin; ofp = (argc > 2) ? my_fopen(argv[2],"w") : stdout; chi_2_test(); return(0); } --- Dr. Dimitri Vulis Brighton Beach Boardwalk BBS, Forest Hills, N.Y.: +1-718-261-2013, 14.4Kbps
participants (2)
-
andreas@artcom.de -
dlv@bwalk.dm.com