shell实现洗牌随机

  洗牌问题:

    洗一副扑克,有什么好办法?既能洗得均匀,又能洗得快?即相对于一个文件来说怎样
高效率的实现乱序排列?

    关于洗牌问题,其实已经有了一个很好的shell解法,这里另外给三个基于AWK的方法,
有错误之处还请不吝指出。



方法一穷举:
    类似于穷举法,构造一个散列来记录已经打印行出现行的次数,如果出现次数多于一
次则不进行处理,这样可以防止重复,但缺点是加大了系统的开销。



awk -v N=`sed -n '$=' data` '
BEGIN{
FS="
";
RS=""
}
{
srand();
while(t!=N){
  x=int(N*rand()+1);
  a[x]++;
  if(a[x]==1)
    {
        print $x;t++
    }
  }
}
' data


方法二变换:
    基于数组下标变换的办法,即用数组储存每行的内容,通过数组下标的变换
交换数组的内容,效率好于方法一。


#! /usr/awk

BEGIN{
srand();
}


{
b[NR]=$0;
}


END{

C(b,NR);
for(x in b)
  {
    print b[x];
  }

}


function C(arr,len,i,j,t,x){

for(x in arr)
  {
      i=int(len*rand())+1;
      j=int(len*rand())+1;
      t=arr[i];
      arr[i]=arr[j];
      arr[j]=t;
  }

}




方法三散列:

    三个方法中最好的。
    利用AWK中散列的特性(详细请看:info gawk 中的7.x ),只要构造一个
随机不重复的散列函数即可,因为一个文件每行的linenumber是独一无二的,所
以用:

    随机数+每行linenumber    ------对应------>    那一行的内容

即为所构造的随机函数。
    从而有:


    awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' data







    其实大家担心的使用内存过大的问题不必太在意,可以做一个测试:

测试环境:
PM 1.4GHz CPU,40G硬盘,内存256M的LAPTOP
SUSE 9.3  GNU bash version 3.00.16 GNU Awk 3.1.4

产生一个五十几万行的随机文件,大约有38M:

od /dev/urandom |dd  count=75000 >data



拿效率较低的方法一来说:

洗牌一次所用时间:

time awk -v N=`sed -n '$=' data` '
BEGIN{
FS="
";
RS=""
}
{
srand();
while(t!=N){
  x=int(N*rand()+1);
  a[x]++;
  if(a[x]==1)
    {
        print $x;t++
    }
  }
}
' data

结果(文件内容省略):


real    3m41.864s
user    0m34.224s
sys     0m2.102s


所以效率还是勉强可以接受的。

方法二的测试:

time awk -f awkfile datafile

结果(文件内容省略):

real    2m26.487s
user    0m7.044s
sys     0m1.371s

效率明显好于第一个。


接着考察一下方法三的效率:

time awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' data

结果(文件内容省略):

real    0m49.195s
user    0m5.318s
sys     0m1.301s


对于一个38M的文件来说已经相当不错了。
玩的愉快!
原文地址:https://www.cnblogs.com/archoncap/p/4964006.html