Home > database >  How to generate equally distributed random numbers in a range and with decimal places in bash?
How to generate equally distributed random numbers in a range and with decimal places in bash?

Time:10-28

How can I generate equally distributed random numbers in a range and with p.e, 2 or 5 decimal places in bash without to use AWK or bc ?

Bash usually only supports whole numbers. However, numbers with decimal places can be used in bash, e.g. with the command sleep: sleep 1.23456 # sleep time in s

Given is the following example, which can generate with bash in a range from 0 to 10, equally distributed random numbers without decimal places.

ug_rnd=0
og_rnd=10

rnd="$((0x$(dd if=/dev/urandom of=/dev/stdout bs=4 count=1 status=none | xxd -p)))"
        my_rnd=$((rnd%(og_rnd-ug_rnd 1) ug_rnd));        
        echo "$my_rnd"

sleep "$my_rnd"

CodePudding user response:

Assume you're currently generating random numbers between 0 and 10 and you want to add 2 decimal places.

Consider generating random numbers between 0 and 1000 (10 * 10^2), split the result into 2x chunks and then piece together with a decimal.

A rough example:

NOTE: Added Kamilcuk's comment re: using printf to take care of left padding numbers with 0's

$ x=735           # assume this is our random number (between 0 and 1000)

$ printf -v newx "%d.d" "$((x/100))" "$((x0))" 
$ echo "${newx}"
7.35

$ time sleep "${newx}"

real    0m7.375s
user    0m0.000s
sys     0m0.015s

If OP wants 4 decimal places then generate a random number between 0 and 100000 (10 x 10^4), and replace the 100 entries (in the newx split-n-piece-together operation) with 10000.

Shouldn't be too hard to add some logic to current code to figure out the multiplier (10^<number_of_decimals>), set og_rnd=$((10*<multiplier)), and replace 100 with the <multiplier> variable in the split-n-piece-together operation.

CodePudding user response:

I played with this while you guys were talking about it. My suggestion was similar, kinda...

 range=100 scale=5; w=$((${#range} scale)); 
 printf "%0$w.${scale}f\n" "$((RANDOM%$range)).$(
   s=$scale; while ((s--)); do printf "%s" $((RANDOM)); done
 )"

I still think awk is a much better solution, though.

CodePudding user response:

Follow working fine for me, based of the answer of markp-fuso and sample on my question, for positive counts (a not for negative counts), which give me equal distributed count with a asked count of decimal places, which can set by a var.

ug_rnd=0
og_rnd=10
decimal_places=2

decimal_places_faktor=$((10**decimal_places))

ug_rnd_new=$((ug_rnd*decimal_places_faktor))
og_rnd_new=$((og_rnd*decimal_places_faktor))

rnd="$((0x$(dd if=/dev/urandom of=/dev/stdout bs=4 count=1 status=none | xxd -p)))"
my_rnd=$((rnd%(og_rnd_new-ug_rnd_new 1) ug_rnd_new));        
echo "$my_rnd"

printf -v my_new_rnd "%d.d" "$((my_rnd/decimal_places_faktor))" "$(($my_rnd           
  • Related