madebyjames
madebyjames

Reputation: 19

Bash random number generator where number is not in array

I am attempting to create a random number generator that generates a number between 1 and 99 but not any number that has already been generated.

In the script array1 contains the numbers already generated. To make it easier to test I have reduced the random number range to 0 - 14 and manually created an array.

I am quite new to bash scripting and am picking it up with a couple of books and the internet.

I have tried a mixture of ideas, the one that seems to make most sense is

array1=( 1 2 3 6 7 8 9 10 11 12 13 )
func1() {
    for var in "${array1[@]}"
    do
      echo $var
    done
}
rnd=$[ $RANDOM % 14 ]
until [ $rnd != func1 ]
    do
        rnd=$[ $RANDOM % 14 ]
    done
echo $rnd

however I know the problem is on line 9 the shell sees the following code:

until [ $rnd != 1 2 3 6 7 8 9 10 11 12 13 ]

I know that the solution is that line 9 needs to be:

until [ $rnd != 1 ] && [ $rnd != 2 ] && [ $rnd != 3 ] && ...

I just don't know how to make this happen automatically from the array. The array does vary in length depending on how many numbers have been generated.

Any help will be greatly appreciated!

Upvotes: 1

Views: 1636

Answers (3)

SiegeX
SiegeX

Reputation: 140227

As noted in one of the comments, using the Knuth Shuffle is an excellent way to do this

#!/bin/bash

shuffle() {
   local i tmp size max rand
   # Code from http://mywiki.wooledge.org/BashFAQ/026
   # $RANDOM % (i+1) is biased because of the limited range of $RANDOM
   # Compensate by using a range which is a multiple of the array size.
   size=${#array[*]}
   max=$(( 32768 / size * size ))

   for ((i=size-1; i>0; i--)); do
      while (( (rand=$RANDOM) >= max )); do :; done
      rand=$(( rand % (i+1) ))
      tmp=${array[i]} array[i]=${array[rand]} array[rand]=$tmp
   done
}

# Fill an array with values 1 to 99
array=({1..99});

# Shuffle the array at random
shuffle

# Echo shuffled array
echo ${array[@]}

Output

$ ./knuth
58 78 6 37 84 79 81 43 50 25 49 56 99 41 26 15 86 11 96 90 76 46 92 70 87 27 33 91 1 2 73 97 65 69 42 32 39 67 72 52 36 64 24 88 60 35 83 89 66 30 4 53 57 28 75 48 40 74 18 23 45 61 20 31 21 16 68 80 62 8 98 14 7 19 47 55 22 85 59 17 77 10 63 93 51 54 95 82 94 9 44 38 13 71 34 29 5 3 12

Upvotes: 1

jordanm
jordanm

Reputation: 34914

This is something that I found difficulty doing in bash. The approach I came up with is to have func1() return true or false and modify the array to remove the number that has been picked.

array=( {1..15} )
func1() {
   local pick="$1"
   found=1
   total=${#array[@]}
   for ((i=0;i<total;i++)); do 
      if (( pick == ${array[i]} )); then
         echo $pick
         array=( ${array[@]:0:i} ${array[@]:((i + 1)):$total})
         found=0
         break
      fi
   done
   return $found
}
numbers=3
for ((x=0;x<numbers;x++)); do 
   until func1 $(( $RANDOM % ( ${#array[@]} ) )); do 
      continue 
   done 
done

Upvotes: 1

choroba
choroba

Reputation: 241758

You can also use the -R switch to sort, if your version of sort supports it:

for x in {1..99} ; do echo $x ; done | sort -R

Upvotes: 0

Related Questions