pintcat

whrandom.sh - POSIX compliant random number generator using Wichmann-Hill method

Oct 29th, 2023 (edited)
1,996
0
Never
10
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 8.33 KB | Science | 0 0
  1. ##################################################################################################################################
  2. # whrandom v2.46 - POSIX compliant random number generator using old or new improved Wichmann-Hill method.                       #
  3. # Handshake:                                                                                                                     #
  4. # $WH_MIN contains the minimum value for the random number (default: 0).                                                         #
  5. # $WH_MAX contains the maximum value for the random number. The limit is 4 billion. Using rejection sampling, the process will   #
  6. #  slow down significantly if the max value exceeds 999999999 (see $WH_SCALE; default: 4000000000).                              #
  7. # $WH_LOOP tells how many random numbers to be generated. Set it to "U" for an unlimited random number generation (default: 1).  #
  8. # $WH_DEL contains the delimiter used to separate multiple values (default: \n (new line)).                                      #
  9. # $WH_ALGO tells which algorithm will be used - WH_OLD, WH_OLD32, WH_NEW or WH_NEW64 (default: WH_OLD32).                        #
  10. # $WH_SCALE sets the scaling mode if $WH_MIN and/or $WH_MAX is used. Leave empty to trigger the slower, but better rejection     #
  11. #  sampling method. Set it to anything to trigger a simpler, but slightly faster rounding method (default: fast).                #
  12. # $WH_INIT contains a string which is used to generate seeds via checksum. (default: current date/time if unset).                #
  13. # Usage:                                                                                                                         #
  14. # Simply call WH_RANDOM() to generate one or more random numbers. These numbers will be written to stdout.                       #
  15. # The old Wichmann-Hill algorrithm requires 3, the new one 4 seeds each between 1 and 30000. By default, whrandom automatically  #
  16. # creates 4 unique seeds. Alternatively, you can call WH_RANDOM() with up to 4 integers. They will be checked and exchanged with #
  17. # automatically generated ones if they don't fit the needs. Note that if you use 4 fixed seeds whrandom will always create an    #
  18. # identical chain of random numbers as long as you use the same seeds.                                                           #
  19. ##################################################################################################################################
  20.  
  21. WH_LOOP=1
  22. WH_DEL="\n"
  23. WH_SCALE=fast
  24.  
  25. WH_CHECK(){ # Checks if a value is given, if this value is a number and if it's between 1 and 30000. If so it returns that very
  26. #             number and if not it generates a suitable one by calculating a checksum from a string in $WH_INIT.
  27. # IN:  $1 should contain an integer between 1 and 30000.
  28. #      $WH_INIT contains a string to generate seeds via checksum. Set it to whatever you want. If empty, the current date/time is
  29. #       used as string. It will rotate left with each iteration to ensure a unique value.
  30. # OUT: Prints the fitting number to stdout.
  31.     if [ -n "${1##*[!0-9]*}" ] && [ $1 -gt 0 ] && [ $1 -le 30000 ]; then
  32.         printf '%s' "$1"
  33.     else # Calculate checksum and then modulo 30000 to narrow it down to the required range. Then rotate 1 sign to the left.
  34.         [ -z "$WH_INIT" ] && WH_INIT=$(date)
  35.         printf '%s' $(($(printf '%s' "$WH_INIT" | cksum | cut -d" " -f1)%30000+1))
  36.         WH_INIT=${WH_INIT#?}${WH_INIT%"${WH_INIT#?}"}
  37.     fi
  38. }
  39.  
  40. WH_OLD(){ # generates a random number using the older 16bit integer based algorithm.
  41. # IN:  $WH_S1, $WH_S2 & $WH_S3 contain the values (seeds) needed by the algorithm for computation.
  42. # OUT: $WH_RND contains the random number to be handed over to the core function for further operations.
  43.     [ $((WH_S1=171*($WH_S1%177)-2*($WH_S1/177))) -lt 0 ] && WH_S1=$(($WH_S1+30269))
  44.     [ $((WH_S2=172*($WH_S2%176)-35*($WH_S2/176))) -lt 0 ] && WH_S2=$(($WH_S2+30307))
  45.     [ $((WH_S3=170*($WH_S3%178)-63*($WH_S3/178))) -lt 0 ] && WH_S3=$(($WH_S3+30323))
  46.     WH_RND=$(($WH_S1*1000000000/30269+$WH_S2*1000000000/30307+$WH_S3*1000000000/30323))
  47. }
  48.  
  49. WH_OLD32(){ # same result as WH_OLD(), but uses 32bit integer based algorithm with less steps & thus being slightly faster.
  50.     WH_S1=$((171*$WH_S1%30269))
  51.     WH_S2=$((172*$WH_S2%30307))
  52.     WH_S3=$((170*$WH_S3%30323))
  53.     WH_RND=$(($WH_S1*1000000000/30269+$WH_S2*1000000000/30307+$WH_S3*1000000000/30323))
  54. }
  55.  
  56. WH_NEW(){ # generates a random number using the newer 32bit integer based algorithm.
  57. # IN:  $WH_S1, $WH_S2, $WH_S3 & $WH_S4 contain the values (seeds) needed by the algorithm for computation.
  58. # OUT: $WH_RND contains the random number to be handed over to the core function for further operations.
  59.     [ $((WH_S1=11600*($WH_S1%185127)-10379*($WH_S1/185127))) -lt 0 ] && WH_S1=$(($WH_S1+2147483579))
  60.     [ $((WH_S2=47003*($WH_S2%45688)-10479*($WH_S2/45688))) -lt 0 ] && WH_S2=$(($WH_S2+2147483543))
  61.     [ $((WH_S3=23000*($WH_S3%93368)-19423*($WH_S3/93368))) -lt 0 ] && WH_S3=$(($WH_S3+2147483423))
  62.     [ $((WH_S4=33000*($WH_S4%65075)-8123*($WH_S4/65075))) -lt 0 ] && WH_S4=$(($WH_S4+2147483123))
  63.     WH_RND=$(($WH_S1*1000000000/2147483579+$WH_S2*1000000000/2147483543+$WH_S3*1000000000/2147483423+$WH_S4*1000000000/2147483123))
  64. }
  65.  
  66. WH_NEW64(){ # same result as WH_NEW(), but uses 64bit integer based algorithm with less steps & thus being slightly faster.
  67.     WH_S1=$((11600*$WH_S1%2147483579))
  68.     WH_S2=$((47003*$WH_S2%2147483543))
  69.     WH_S3=$((23000*$WH_S3%2147483423))
  70.     WH_S4=$((33000*$WH_S4%2147483123))
  71.     WH_RND=$(($WH_S1*1000000000/2147483579+$WH_S2*1000000000/2147483543+$WH_S3*1000000000/2147483423+$WH_S4*1000000000/2147483123))
  72. }
  73.  
  74. WH_RANDOM(){ # core function: prepares seeds and generates the output.
  75. # IN:  $1, $2, $3 and $4 may contain integers between 1 and 30000 to be used as seeds. These values will be checked and - if not
  76. #       suitable - replaced with auto generated ones.
  77. #      $WH_LOOP tells how many numbers will be generated. "U" will generate a never ending chain of random numbers.
  78. #      $WH_RND contains the random number which was handed over by the algorithm for further operations.
  79. #      $WH_MIN contains the minimum value which will not be underrun by the random number.
  80. #      $WH_MAX contains the maximum value which will not be exceeded by the random number.
  81. #      $WH_ALGO sets one of the four algorithms: WH_OLD, WH_OLD32, WH_NEW & WH_NEW64. Anything else will set the default WH_OLD32.
  82. #      $WH_DEL contains the delimiter which is placed after each value.
  83. #      $WH_SCALE tells which method is used to scale the random number. If left empty, rejection sampling is used which is slower, but
  84. #       with better diffusion & no bias. If set to anything, it uses a slightly faster rounding method with a little worse diffusion.
  85. # OUT: $WH_SEEDS contains the original seeds used for the 1st iteration.
  86. #      $WH_RNG is the range from $WH_MIN to $WH_MAX.
  87. #      $WH_LMT is the largest multiple of $WH_RNG which is still < or = 1000000000.
  88. #      $WH_LO contains the lower block (everything below 1000000000 if $WH_MAX exceeds 999999999).
  89. #      $WH_HI contains the block index to decide which range above 999999999 is used in addition to $WH_LO.
  90. #      $WH_RND contains the current random number
  91. #      Also prints the random numbers with delimiter appended to stdout.
  92.     WH_TMP=1
  93.     [ -z "${WH_MAX##*[!0-9]*}" ] || [ $WH_MAX -le 0 ] || [ $WH_MAX -gt 4000000000 ] && WH_MAX=4000000000
  94.     [ -z "${WH_MIN##*[!0-9]*}" ] || [ $WH_MIN -le 0 ] || [ $WH_MIN -ge $WH_MAX ] && WH_MIN=0
  95.     case $WH_ALGO in
  96.         WH_OLD|WH_OLD32|WH_NEW|WH_NEW64);;
  97.         *) WH_ALGO=WH_OLD32;;
  98.     esac
  99.     set -- $(WH_CHECK "$1") $(WH_CHECK "$2") $(WH_CHECK "$3") $(WH_CHECK "$4")
  100.     WH_S1=$1 WH_S2=$2 WH_S3=$3 WH_S4=$4
  101.     WH_SEEDS="$WH_S1 $WH_S2 $WH_S3 $WH_S4"
  102.     while [ $WH_LOOP = U ] || [ $((WH_TMP=$WH_TMP+1)) -le $(($WH_LOOP+1)) ]; do
  103.         $WH_ALGO
  104.         if [ -z $WH_SCALE ]; then
  105.             if [ $((WH_RNG=$WH_MAX-$WH_MIN+1)) -gt 1000000000 ]; then
  106.                 WH_LO=$(($WH_RND%1000000000))
  107.                 $WH_ALGO
  108.                 WH_HI=$(($WH_RND%(($WH_RNG+999999999)/1000000000)))
  109.                 [ $((WH_RND=$WH_HI*1000000000+$WH_LO)) -lt $WH_RNG ] && printf $(($WH_RND+$WH_MIN))"$WH_DEL" || WH_TMP=$(($WH_TMP-1))
  110.             else
  111.                 WH_LMT=$((1000000000/$WH_RNG*$WH_RNG))
  112.                 [ $((WH_RND=$WH_RND%1000000000)) -lt $WH_LMT ] && printf $(($WH_RND%$WH_RNG+$WH_MIN))"$WH_DEL" || WH_TMP=$(($WH_TMP-1))
  113.             fi
  114.         else
  115.             printf $(((($WH_RND%1000000000*($WH_MAX-$WH_MIN)+555555555)/1000000000)+$WH_MIN))"$WH_DEL"
  116.         fi
  117.     done
  118. }
  119.  
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment