【问题标题】:Bash random number generator where number is not in arrayBash 随机数生成器,其中数字不在数组中
【发布时间】:2012-02-01 04:17:53
【问题描述】:

我正在尝试创建一个随机数生成器,它生成一个介于 1 到 99 之间的数字,但不是任何已经生成的数字。

在脚本中 array1 包含已经生成的数字。为了更容易测试,我将随机数范围减少到 0 - 14 并手动创建了一个数组。

我对 bash 脚本还很陌生,并且通过几本书和互联网来学习它。

我尝试了各种想法,似乎最有意义的一个是

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

但是我知道问题出在第 9 行,shell 看到以下代码:

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

我知道解决方案是第 9 行需要:

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

我只是不知道如何从数组中自动实现这一点。数组的长度会根据生成的数字数量而有所不同。

任何帮助将不胜感激!

【问题讨论】:

  • 查找“随机排列”和“Knuth shuffle”。
  • 不推荐使用rnd=$[ $RANDOM % 14 ] 形式。请改用((rnd = $RANDOM % 14))。如果你有shuf 实用程序,你可以使用shuf -i 0-14

标签: arrays bash random numbers


【解决方案1】:

正如其中一个 cmets 所述,使用 Knuth Shuffle 是一种很好的方法

#!/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[@]}

输出

$ ./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

【讨论】:

    【解决方案2】:

    这是我在 bash 中难以做到的事情。我想出的方法是让 func1() 返回 true 或 false 并修改数组以删除已选择的数字。

    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
    

    【讨论】:

    • 你测试了吗?我在 bash 3 中得到一个 syntax error near unexpected token (': until func1 (( $RANDOM % ${array[-1]} )); do
    • 看起来我在复制时遗漏了一个“$”。为了便于阅读,我将所有内容都写在了一行中。
    • 您可以省略数组切片中的(()) 和美元符号:array=( ${array[@]:0:i} ${array[@]:i + 1:total})。您也可以unset 一个数组元素,但这会创建一个稀疏数组,并且您必须使用${!array[@]} 对其索引进行迭代。
    • @DennisWilliamson 感谢您提供有关 ${!array[@]} 的信息。我没有使用 unset 因为我不知道 ${!array[@]}。
    【解决方案3】:

    如果您的sort 版本支持,您也可以使用-R 切换到sort

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

    【讨论】:

    • printf '%s\n' {1..99} | sort -R
    猜你喜欢
    • 2010-11-14
    • 2018-04-16
    • 1970-01-01
    • 2018-10-18
    • 2021-12-31
    • 2012-06-05
    • 2017-05-02
    • 2012-03-15
    • 1970-01-01
    相关资源
    最近更新 更多