How to generate random number in Bash?

前端 未结 11 1535
青春惊慌失措
青春惊慌失措 2020-11-27 09:33

How to generate a random number within a range in Bash?

11条回答
  •  不知归路
    2020-11-27 10:29

    I have taken a few of these ideas and made a function that should perform quickly if lots of random numbers are required.

    calling od is expensive if you need lots of random numbers. Instead I call it once and store 1024 random numbers from /dev/urandom. When rand is called, the last random number is returned and scaled. It is then removed from cache. When cache is empty, another 1024 random numbers is read.

    Example:

    rand 10; echo $RET
    

    Returns a random number in RET between 0 and 9 inclusive.

    declare -ia RANDCACHE
    declare -i RET RAWRAND=$(( (1<<32)-1 ))
    
    function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
      local -i N=$1
      [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
      RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
      unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
    };
    
    # test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.
    
    declare -i c; declare -ia BIN
    
    for (( c=0; c<100000; c++ )); do
      rand 10
      BIN[RET]+=1  # add to bin to check distribution
    done
    
    for (( c=0; c<10; c++ )); do
      printf "%d %d\n" $c ${BIN[c]} 
    done
    

    UPDATE: That does not work so well for all N. It also wastes random bits if used with small N. Noting that (in this case) a 32 bit random number has enough entropy for 9 random numbers between 0 and 9 (10*9=1,000,000,000 <= 2*32) we can extract multiple random numbers from each 32 random source value.

    #!/bin/bash
    
    declare -ia RCACHE
    
    declare -i RET             # return value
    declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
    declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit
    
    declare -i BYTES=4         # size of unsigned random bytes returned by od
    declare -i BITS=8*BYTES    # size of random data returned by od in bits
    declare -i CACHE=16        # number of random numbers to cache
    declare -i MAX=2**BITS     # quantum of entropy per cached random number
    declare -i c
    
    function rand(){  # pick a random number from 0 to 2^BITS-1
      [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
      RET=${RCACHE[-1]}              # pull last random number and scale
      unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
    };
    
    function randBetween(){
      local -i N=$1
      [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
        rand; RND=RET       # get more random bits
        ENT=MAX             # reset entropy
      }
      RET=RND%N  # random number to return
      RND=RND/N  # remaining randomness
      ENT=ENT/N  # remaining entropy
    };
    
    declare -ia BIN
    
    for (( c=0; c<100000; c++ )); do
      randBetween 10
      BIN[RET]+=1
    done
    
    for c in ${BIN[*]}; do
      echo $c
    done
    

提交回复
热议问题