Home > Enterprise >  How can I generate equally distributed random numbers in a range and with decimal places in bash?
How can I 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:

Only a partly and I hope its not the best solution is to run the sample code from question two times on follow way:

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_1=$((rnd%(og_rnd-ug_rnd 1) ug_rnd));        


ug_rnd=0
og_rnd=10000

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


echo "$my_rnd_1.$my_rnd_2" 
sleep "$my_rnd_1.$my_rnd_2"

This partly solution create in a range of 0 to 10 the right output, a additional a not wanted output between 10 up to 10.99999

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.

  • Related