Reputation: 15
I want to generate a random nunmber between some values but except some values.
I have this:
shuf -i 1-9 -n 1
Or this:
$(( ( RANDOM % 10 ) + 1 ))
But i want to exclude the number 3 and the number 8, for example.
Upvotes: 0
Views: 1600
Reputation: 8074
A simple way to select a random value from any set of values is to put the values in an array and select a random item from the array. In this case:
values=(1 2 {4..7} 9)
random_value=${values[RANDOM % ${#values[*]}]}
Also see Select a random item from an array.
The code above has some limitations. First, it doesn't work if the number of values to select from is greater than 32768, because the maximum value of $RANDOM
is 32767. (The size of the array could become a problem for numbers greater than that anyway.) Second, the value returned by RANDOM % ${#values[*]}
is (usually slightly) biased towards the lower array indices. See Why do people say there is modulo bias when using a random number generator? for more information. If that matters to you, see the rand
function in BashFAQ/026 (How can I randomize/shuffle the order of lines in a file? ...) for Bash code that generates unbiased (pseudo)random numbers in a restricted range.
Upvotes: 1
Reputation: 15273
A function?
rnd() {
local n=$(( $RANDOM % 10 ))
while [[ $n =~ [038] ]]
do n=$(( $RANDOM % 10 ))
done
echo $n
}
Then you can just say x=$( rnd )
.
Upvotes: 1
Reputation: 27215
Instead of specifying a range of numbers you can specify each valid number individually using the -e
option (at least for shuf
from GNU coreutils) or you can specify the numbers in an (anonymous) file. Since the last option is more portable (works also for shuffle
on BSD), we will use that. Now we only have to generate output with the numbers 1, 2, 4, 5, 6, 7, 9. Here are some examples:
shuf -n 1 <(seq 9 | grep -Fxv -e{3,8})
shuf -n 1 <(printf %s\\n 1 2 4 5 6 7 9)
shuf -n 1 <(printf %s\\n 1 2 {4..7} 9)
shuf -n 1 <(seq 2; seq 4 7; echo 9)
For each finite set S={i1,i2,…in}⊂ℕ of integers you can come up (by hand) with a polynomial f such that f(x)=ix for all x∈{0,1,…,n-1}.
In general, polynomial f has degree n-1. Sometimes we can use rounding to reduce the degree. In your concrete case of S={1,2,4,5,6,7,9} we can use f(x) = floor(1.25x+1.5). Here is an implementation in bash
using bc
(the (…) / 1
rounds down).
bc <<< "($RANDOM % 7 * 1.25 + 1.5) / 1"
A benefit of this method is that it works purley with built-ins if we scale all numbers inside the formula such that they become integers (here we scaled by a factor of 4).
echo $(( ($RANDOM % 7 * 5 + 6) / 4 ))
Upvotes: 3
Reputation: 14454
Here is one way:
until N=$(( ( RANDOM % 10 ) + 1 )); (( $N != 3 && $N != 8 )); do true; done; echo $N
One could argue that this way is imperfectly efficient, but in the absence of an obvious, simple alternative, it should suit.
Upvotes: 1