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.