RNG147
March 28, 2017
We have looked at random number generators in several previous exercises, but most of them returned integers. In today’s exercise we look at a simple random number generator that returns floating-point numbers. The generator is simple: Given a seed between zero and one, the next number in the sequence is the fractional portion of 147 times the seed.
Your task is to implement the random number generator described above, and to assess its suitability. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
In python 3
Your sine trick only moves the problem elsewhere; try seeding with pi/6 (arcsin of 1/2).
Solution in Haskell, using the Stream package for explicitly infinite lists:
Here’s another Haskell version. With an integer argument, n, it prints the first n random numbers. With no arguments it converts the stream of random Doubles into a stream of 32-bit unsigned integers. The purpose is to feed that binary data to the dieharder program, which evaluates its randomness. We don’t make use of all the bits in the Double; it’s just an easy way of getting the data to dieharder in a form that it likes.
{-# LANGUAGE ScopedTypeVariables #-} import Control.Monad (void) import Data.Word (Word32) import Foreign.Marshal.Alloc (alloca) import Foreign.Storable (poke, sizeOf) import System.Environment (getArgs) import System.IO (hPutBuf, stdout) -- A stream of "random" numbers. (We take the tail to exclude the seed from the -- values.) rng147 :: RealFrac b => b -> [b] rng147 = tail . iterate step where step x = let (_ :: Int, f) = properFraction (147 * x) in f main :: IO () main = do let rs = rng147 (0.1234567 :: Double) ns <- fmap (map read) getArgs :: IO [Int] case ns of -- Output an endless stream of 32-bit binary data for dieharder. [] -> put $ map cvt rs -- Print the first n random values. [n] -> mapM_ print $ take n rs -- Invalid argument(s). _ -> putStrLn "Usage: rng147 [n]" -- Convert a value to an 32-bit unsigned integer, possibly throwing away the -- least significant bits of the argument. cvt :: RealFrac a => a -> Word32 cvt x = round $ fromIntegral (maxBound :: Word32) * x -- Output the unsigned 32-bit values as binary data. put :: [Word32] -> IO () put is = let n = sizeOf (0 :: Word32) in alloca (\p -> void (mapM_ (\i -> poke p i >> hPutBuf stdout p n) is))Here’s the result of feeding the output to dieharder. I won’t try to interpret the results, here. (The -a argument tells it to run all the tests it has; -g 200 says that stdin is a series of 32-bit unsigned integers.) The summary is that 82 tests passed, 27 failed and 5 were weak.
$ ./rng147 | dieharder -a -g 200 #=============================================================================# # dieharder version 3.31.1 Copyright 2003 Robert G. Brown # #=============================================================================# rng_name |rands/second| Seed | stdin_input_raw| 1.01e+07 |2776686247| #=============================================================================# test_name |ntup| tsamples |psamples| p-value |Assessment #=============================================================================# diehard_birthdays| 0| 100| 100|0.64044752| PASSED diehard_operm5| 0| 1000000| 100|0.00000000| FAILED diehard_rank_32x32| 0| 40000| 100|0.45366134| PASSED diehard_rank_6x8| 0| 100000| 100|0.97658733| PASSED diehard_bitstream| 0| 2097152| 100|0.72508491| PASSED diehard_opso| 0| 2097152| 100|0.00000000| FAILED diehard_oqso| 0| 2097152| 100|0.00000000| FAILED diehard_dna| 0| 2097152| 100|0.00000000| FAILED diehard_count_1s_str| 0| 256000| 100|0.00000000| 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.00000000| FAILED diehard_3dsphere| 3| 4000| 100|0.00000000| FAILED diehard_squeeze| 0| 100000| 100|0.00000000| FAILED diehard_sums| 0| 100| 100|0.06709806| PASSED diehard_runs| 0| 100000| 100|0.00000000| FAILED diehard_runs| 0| 100000| 100|0.00000000| FAILED diehard_craps| 0| 200000| 100|0.13428702| PASSED diehard_craps| 0| 200000| 100|0.00096831| WEAK marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED sts_monobit| 1| 100000| 100|0.03994929| PASSED sts_runs| 2| 100000| 100|0.22979976| PASSED sts_serial| 1| 100000| 100|0.98471576| PASSED sts_serial| 2| 100000| 100|0.31624292| PASSED sts_serial| 3| 100000| 100|0.13274265| PASSED sts_serial| 3| 100000| 100|0.06274970| PASSED sts_serial| 4| 100000| 100|0.10906298| PASSED sts_serial| 4| 100000| 100|0.71359846| PASSED sts_serial| 5| 100000| 100|0.00790811| PASSED sts_serial| 5| 100000| 100|0.07231695| PASSED sts_serial| 6| 100000| 100|0.17659272| PASSED sts_serial| 6| 100000| 100|0.18513720| PASSED sts_serial| 7| 100000| 100|0.04315090| PASSED sts_serial| 7| 100000| 100|0.45634930| PASSED sts_serial| 8| 100000| 100|0.14826946| PASSED sts_serial| 8| 100000| 100|0.42520026| PASSED sts_serial| 9| 100000| 100|0.71305937| PASSED sts_serial| 9| 100000| 100|0.20822407| PASSED sts_serial| 10| 100000| 100|0.97154428| PASSED sts_serial| 10| 100000| 100|0.69812982| PASSED sts_serial| 11| 100000| 100|0.53454147| PASSED sts_serial| 11| 100000| 100|0.08444014| PASSED sts_serial| 12| 100000| 100|0.22453592| PASSED sts_serial| 12| 100000| 100|0.72236320| PASSED sts_serial| 13| 100000| 100|0.78366045| PASSED sts_serial| 13| 100000| 100|0.37616667| PASSED sts_serial| 14| 100000| 100|0.95764809| PASSED sts_serial| 14| 100000| 100|0.14472891| PASSED sts_serial| 15| 100000| 100|0.43901688| PASSED sts_serial| 15| 100000| 100|0.76714556| PASSED sts_serial| 16| 100000| 100|0.58587727| PASSED sts_serial| 16| 100000| 100|0.67425978| PASSED rgb_bitdist| 1| 100000| 100|0.05846675| PASSED rgb_bitdist| 2| 100000| 100|0.00004623| WEAK rgb_bitdist| 3| 100000| 100|0.66589418| PASSED rgb_bitdist| 4| 100000| 100|0.00026565| WEAK rgb_bitdist| 5| 100000| 100|0.07969442| PASSED rgb_bitdist| 6| 100000| 100|0.17850326| PASSED rgb_bitdist| 7| 100000| 100|0.25911992| PASSED rgb_bitdist| 8| 100000| 100|0.00045592| WEAK rgb_bitdist| 9| 100000| 100|0.00642740| PASSED rgb_bitdist| 10| 100000| 100|0.27514527| PASSED rgb_bitdist| 11| 100000| 100|0.91420132| PASSED rgb_bitdist| 12| 100000| 100|0.09738292| PASSED rgb_minimum_distance| 2| 10000| 1000|0.00000000| FAILED rgb_minimum_distance| 3| 10000| 1000|0.00000000| FAILED rgb_minimum_distance| 4| 10000| 1000|0.00000000| FAILED rgb_minimum_distance| 5| 10000| 1000|0.01232138| PASSED rgb_permutations| 2| 100000| 100|0.55323173| PASSED rgb_permutations| 3| 100000| 100|0.00000000| FAILED rgb_permutations| 4| 100000| 100|0.00000000| FAILED rgb_permutations| 5| 100000| 100|0.00000000| FAILED rgb_lagged_sum| 0| 1000000| 100|0.76792249| PASSED rgb_lagged_sum| 1| 1000000| 100|0.59838659| PASSED rgb_lagged_sum| 2| 1000000| 100|0.95607401| PASSED rgb_lagged_sum| 3| 1000000| 100|0.28565715| PASSED rgb_lagged_sum| 4| 1000000| 100|0.95085961| PASSED rgb_lagged_sum| 5| 1000000| 100|0.45582064| PASSED rgb_lagged_sum| 6| 1000000| 100|0.60565985| PASSED rgb_lagged_sum| 7| 1000000| 100|0.04195406| PASSED rgb_lagged_sum| 8| 1000000| 100|0.30831976| PASSED rgb_lagged_sum| 9| 1000000| 100|0.36546234| PASSED rgb_lagged_sum| 10| 1000000| 100|0.19936994| PASSED rgb_lagged_sum| 11| 1000000| 100|0.07303233| PASSED rgb_lagged_sum| 12| 1000000| 100|0.34645127| PASSED rgb_lagged_sum| 13| 1000000| 100|0.29195838| PASSED rgb_lagged_sum| 14| 1000000| 100|0.30094975| PASSED rgb_lagged_sum| 15| 1000000| 100|0.99790005| WEAK rgb_lagged_sum| 16| 1000000| 100|0.75196523| PASSED rgb_lagged_sum| 17| 1000000| 100|0.38809626| PASSED rgb_lagged_sum| 18| 1000000| 100|0.61180091| PASSED rgb_lagged_sum| 19| 1000000| 100|0.19440479| PASSED rgb_lagged_sum| 20| 1000000| 100|0.57917094| PASSED rgb_lagged_sum| 21| 1000000| 100|0.83129601| PASSED rgb_lagged_sum| 22| 1000000| 100|0.57292407| PASSED rgb_lagged_sum| 23| 1000000| 100|0.10381929| PASSED rgb_lagged_sum| 24| 1000000| 100|0.93651195| PASSED rgb_lagged_sum| 25| 1000000| 100|0.50848729| PASSED rgb_lagged_sum| 26| 1000000| 100|0.81756629| PASSED rgb_lagged_sum| 27| 1000000| 100|0.29194323| PASSED rgb_lagged_sum| 28| 1000000| 100|0.14934239| PASSED rgb_lagged_sum| 29| 1000000| 100|0.96722837| PASSED rgb_lagged_sum| 30| 1000000| 100|0.41045537| PASSED rgb_lagged_sum| 31| 1000000| 100|0.33379469| PASSED rgb_lagged_sum| 32| 1000000| 100|0.67528985| PASSED rgb_kstest_test| 0| 10000| 1000|0.12422629| PASSED dab_bytedistrib| 0| 51200000| 1|1.00000000| FAILED dab_dct| 256| 50000| 1|0.00000000| FAILED Preparing to run test 207. ntuple = 0 dab_filltree| 32| 15000000| 1|0.00000000| FAILED dab_filltree| 32| 15000000| 1|0.00000000| FAILED Preparing to run test 208. ntuple = 0 dab_filltree2| 0| 5000000| 1|0.00000000| FAILED dab_filltree2| 1| 5000000| 1|0.00000000| FAILED Preparing to run test 209. ntuple = 0 dab_monobit2| 12| 65000000| 1|1.00000000| FAILED $[…] built several random number generators: [1], [2], [3], [4], [5], [6], [7], [8], [9] (I didn’t realize it was so many until I went back and looked). In today’s exercise we […]