【问题标题】:Random element from an array bigger than 32767 in bashbash中大于32767的数组中的随机元素
【发布时间】:2017-09-02 15:26:55
【问题描述】:

拥有:

mapfile -t words < <( head -10000 /usr/share/dict/words)
echo "${#words[@]}" #10000
r=$(( $RANDOM % ${#words[@]} ))
echo "$r ${words[$r]}"

这会从 10k 个单词的数组中随机选择一个单词。

但是如果数组大于 32767(例如,整个文件 200k+ 字),它会停止工作,因为 $RANDOM 最多只能达到 32767。来自 man bash

每次引用此参数时,都会生成一个介于 0 和 32767 之间的随机整数。

mapfile -t words < /usr/share/dict/words
echo "${#words[@]}" # 235886
r=$(( $RANDOM % ${#words[@]} )) #how to change this?
echo "$r ${words[$r]}"

不想使用像perl -plE 's/.*/int(rand()*$_)/e' 这样的perl,不是每个系统都安装了perl。寻找最简单的解决方案——也不关心真正的随机性——它不适用于密码学。 :)

【问题讨论】:

    标签: arrays bash random


    【解决方案1】:

    一种可能的解决方案是对$RANDOM 的结果进行一些数学运算:

    big_random=`expr $RANDOM \* 32767 + $RANDOM`
    

    另一种方法是使用$RANDOM 一次选择输入文件的一个块,然后再次使用$RANDOM 从该块中选择一行。

    请注意,$RANDOM 不允许您指定范围。 % 给出不统一的结果。进一步讨论:How to generate random number in Bash?

    顺便说一句,将整个words 读入内存似乎并不是特别明智。除非您要对这个数据结构进行大量重复访问,否则请考虑尝试这样做,而不是一次吞下整个文件。

    【讨论】:

    • 你最好像sed -n "${num}p" file这样使用?例如运行读取文件的外部程序?地图文件是内置的,我可以简单地清除数组......或者错过了什么?
    • 我会使用sed,正如你所指出的。 mapfile 将消耗至少与文件大小一样多的进程内存(尽管是暂时的)。 sed 一次会消耗一行的值。你是否同意这是你的决定。
    • ...如果您为运行外部程序而烦恼,您就不会在 Bash 中编程,对吧?
    • :) 我不介意,只是第一次我需要运行wc -l 来获取字数,第二次需要运行sed - 因此我使用了地图文件。但是,会做一些测试 - 并且会看到。感谢这个想法。 :)
    • 如果 shell 完全有能力做这个数学运算,为什么还要调用 expr:big_random=$((32768*RANDOM+RANDOM))。是的,乘数应该是 32768(不是 32767)。或者,如果您想避免乘法,可以使用更快的移位:big_random=$(((RANDOM&lt;&lt;15)+RANDOM))
    【解决方案2】:

    如果shuf 在您的系统上可用...

    r=$(shuf -i 0-${#words[@]} -n 1)
    

    如果没有,您可以多次使用$RANDOM 并连接结果以获得一个具有足够位数的数字来满足您的需求。您应该连接而不是相加,因为添加随机数不会产生均匀分布(就像投掷两个随机骰子会产生总共 7 个而不是总共 1 个)。

    例如:

    printf -v r1 %05d $RANDOM
    printf -v r2 %05d $RANDOM
    printf -v r3 %05d $RANDOM
    r4=${r1:1}${r2:1}${r3:1}
    r=$(( $r4 % ${#words[@]} ))
    

    printf 语句用于确保保留前导零; -v 选项是一个隐藏的宝石,它允许为变量分配值(除其他外,它可以允许在许多有用的现实生活中避免使用 eval)。 r1r2r3 中的第一个数字被删除,因为它只能是 0、1、2 或 3。

    【讨论】:

    • 适用于 Linux - 但遗憾的是,shuf 默认情况下在 MacOSX 上不可用。 :(
    【解决方案3】:

    接受的答案将为您提供十位数,但对于每个五位数前缀,最后五位数可能仅在00000-32767 范围内。

    例如,数字1234567890 是不可能的,因为67890 &gt; 32767

    那可能没问题。我个人觉得这个选项更好一些。它为您提供号码0-1073676289,没有任何间隔。

    big_random=$(expr $RANDOM \* $RANDOM)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-23
      • 2013-06-17
      • 2015-02-08
      • 1970-01-01
      • 2014-06-19
      • 2013-11-14
      • 2015-10-02
      • 1970-01-01
      相关资源
      最近更新 更多