0

With RANDOM%xone gets a set of discrete results.

How can I achieve following scenario?: Inside a for loop, I'd like to let a bash script execute command A in 33.33% of all loops and else execute command B (66.66%).

  • Why is modulo 3 not adequate? – Michael Homer Jul 9 '15 at 6:52
  • What is RANDOM%x? I don't have this tool for shell scripts. And what do you mean by continuos? – ikrabbe Jul 9 '15 at 7:07
  • Over how small (or large) a sample size do you want to guarantee 1:2 ratio of A and B? A random number generator could produce AAA over three runs; is this acceptable? If you require one A and two B values in every three runs, it's important to know this before formulating a solution. – roaima Jul 9 '15 at 15:20
2

You use RANDOM % x, just like you said.

if [[ $((RANDOM % 3)) == 0 ]]
then
    A
else
    B
fi

⅓ of the time the value modulo 3 will be 0, and then command A is executed. The rest of the time, command B is executed.

  • 1
    Strictly speaking this is not a perfect 1/3 to 2/3 ratio. RANDOM generates values 0..32767 inclusive. Modulo 3 gives values in the range 0..2. However, if you modulo 32767 %3 you'll get 1, so values 0 and 1 will have a higher statistical occurrence than 2. (However, for many purposes this will be an insignificant error.) – roaima Jul 9 '15 at 15:14
2

For example, to get a decimal random number from the pseudo random number generator (prng) you can use

dd bs=1 count=1 if=/dev/urandom 2>/dev/null|od -i|awk '{print $2}'

As Michael proposed, you can use modulo 3 on this number, or you can just use

let b=`dd bs=1 count=1 if=/dev/urandom 2>/dev/null|od -i|awk '{print $2}'`
if [ $b -ge 85 ]
then B
else A
fi

With this you have 33.33% <85 and 66.66% >= 85, so you branch on 85.

As I used dc (the polish reverse notation calculator) anyway you can of course just use the modulo

let b=`dd bs=1 count=1 if=/dev/urandom 2>/dev/null|od -t u1|awk '{print $2" 3%pq"}'|dc`
if [ $b -eq 0 ]
then A
else B
fi
  • Your first approach (random byte 0..255 less than 85) is approximately correct but your second is not. "To get a number range divisible by 3 you need 3 bytes" is not true. If you add three numbers each uniform random over 0..255 the sum is NOT uniform over 0..767 (or even 0..765, the actual range); the probability it is less-than 256 is only 16.86% and the best approximation to 33.33% is less-than 325. For a start on understanding why see math.stackexchange.com/a/431375 . – dave_thompson_085 Jul 9 '15 at 14:35
  • Yes, you are right. It was early in the morning. I need two bytes and mask take only the next two bits from the upper one. But that makes it too complex. The modulo 3 version is the best anyway. – ikrabbe Jul 9 '15 at 14:38
1

You should not use a random number to get the desired 33% vs 66% results.

Just increase a counter and use the modulo 3 as mentioned by some others before:

#!/bin/bash

count=0 ca=0  cb=0
while ((count++)); ((count <100)); do
    if [[ $(($count % 3)) == 0 ]]
    then
        ((ca++))
    else
        ((cb++))
    fi
done
printf "count of ca: %d\ncount of cb: %d" ${ca} ${cb}

This results in:

count of ca: 33
count of cb: 66

When a randomizer is used the results are unexpected (not 33% vs 66%):

#with a changed if statement:
#if [[ $(($RANDOM % 3)) == 0 ]]
#the results of three runs are:
count of ca: 31
count of cb: 68

count of ca: 27
count of cb: 72

count of ca: 44
count of cb: 55
  • Your sample size was not large enough for the statistical flattening of 1/3 to 2/3. The output AAA is perfectly acceptable for a random number generator, as is ABB or even BBB. – roaima Jul 9 '15 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.