我有一个需要改组的数组,我想通过使用Awk来优化此算法的速度。我使用Awk还是比较新的,我试图找出模拟这种算法的最佳方法。怎么能这样做呢?
Bash Shuffle:
shuffle() {
local size limit rand i
size=${#password[*]}
limit=$(( 32768 / size * size))
for ((i=size-1; i > 0; i--)); do
while (((rand=$RANDOM) >= limit)); do :; done
rand=$((rand % (i+1)))
tmp=${password[i]}
password[i]=${password[rand]}
password[rand]=$tmp
done
}
Awk Attempt:
shuffle() {
local size limit rand i
size=${#password[*]}
limit=$(( 32768 / size * size))
awk -v rand=$RANDOM 'BEGIN {
srand(rand);
for(i=size-1; i>0; i--) {
while(rand >= limit);
rand=rand % i + 1;
tmp=password[i];
password[i]=password[rand];
password[rand]=tmp;
}
}'
}
答案 0 :(得分:1)
awk具有rand
函数,该函数生成介于0.0和1.0之间的随机数(实际上,严格小于1.0)。要获取[0, i+1)
范围内的随机整数,请使用int(rand()*(i+1))
。我不认为srand
做你认为它做的事情; srand
为awk的随机数生成器设置“种子”,这样可以避免每次调用awk
时生成相同的随机数序列。通常情况下,种子是根据经常变化的东西设置的,比如时间 - 虽然这不是理想的 - 或者是从/dev/random
中提取的值。
有几点意见:
1)我理解你的循环
while (((rand=$RANDOM) >= limit)); do :; done
试图避免$RANDOM
生成的随机数中的偏差,因为该数字只有16位,因此偏差可能会很明显。但是,由于i+1 == size
是基于limit
计算的size
,因此只会避免第一次在循环中出现偏差。之后,limit
将是错误的值。您可以改进计算,或者您可以使用/dev/urandom
生成具有更多随机位的随机数,但我个人只是使用shuf
实用程序,它可以执行您想要的操作(随机输入随机数) )。当然,这比说教更务实;它不像写自己的洗牌一样有教育意义。
2)这同样适用于awk
解决方案(即,当您可以使用awk
时,为什么要使用shuf
?)。但无论如何,从awk
魔法rand
中分配bash
变量$RANDOM
并不会使rand
变得神奇。 awk
和bash
没有任何关联。 (而且你不能在awk脚本中使用像limit
这样的bash变量。)
答案 1 :(得分:0)
当您希望提高速度时..." shuf"而不是使用这些方法中的任何一种。实用程序应该提供随机shuffle的首选实现。
password=( $(printf '%s\0' "${password[@]}" | shuf -z | xargs -0) )
如果关注shuffle的安全性,则使用外部随机源是可选的(可能会牺牲执行速度)。
password=( $(printf '%s\0' "${password[@]}" | shuf -z --random-source=/dev/random | xargs -0) )
答案 2 :(得分:0)
这是shuffle的awk实现:
档案 a.awk :
function get_rand(max) {
return int(rand()*max)
}
function get_array_length(a) {
k=0
for( i in a) k++
return k
}
function arr2str(a) {
astr=""
for(i in a)
astr=((astr)(a[i])" ")
return astr
}
function shuffle_array(in_array) {
array_size=get_array_length(in_array);
## Initialize the random indexing array
for (i=1;i<=array_size;i++)
rand_select[i]=in_array[i]
ridsz=array_size
for(i=1;i<=array_size;i++) {
ridx=get_rand(ridsz)+1;
newarr[i]=rand_select[ridx];
rand_select[ridx]=rand_select[ridsz] ## Move last element, preserve indx
delete rand_select[ridsz];
ridsz--;
}
return arr2str(newarr);
}
BEGIN {
"date +%N"|getline rseed;
srand(rseed);
close("date +%N");
split(vstr, varr, " ");
split(shuffle_array(varr),shuf_varr, " ");
for (element in shuf_varr)
print "got:",shuf_varr[element]
}
然后将其称为:awk -vvstr="$(echo {1..10000})" -f /path/to/a.awk
它没有太多优化,我会留下更多的东西 - 在我的机器上它每10000条记录运行~0.25秒。