用Awk洗牌

时间:2013-05-12 01:01:03

标签: linux algorithm bash shell awk

我有一个需要改组的数组,我想通过使用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;
  }
}'
}

3 个答案:

答案 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变得神奇。 awkbash没有任何关联。 (而且你不能在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秒。