Shell脚本实现乱序排列文件内容的多种方法(洗牌问题)
洗牌问题:洗一副扑克,有什么好办法?既能洗得均匀,又能洗得快?即相对于一个文件来说怎样高效率的实现乱序排列?
ChinaUnix确实是Shell高手云集的地方,只要你想得到的问题,到那里基本上都能找到答案。r2007给出了一个取巧的方法,利用Shell的$RANDOM变量给原文件的每一行加上随机的行号然后根据这个随机行号进行排序,再把临时加上去的行号给过滤掉,这样操作之后得到的新文件就相当于被随机“洗”了一次:
whilereadi;doecho"$i$RANDOM";done<file|sort-k2n|cut-d""-f1
当然如果你的源文件每行的内容比较复杂的话就必须对这段代码进行改写,但只要知道了处理的关键技巧,剩下的问题都不难解决。
另外一篇来自苏蓉蓉的用awk来实现洗牌效果的随机文件排序代码分析(原贴在这里,以及对此帖的一个后续讨论,如果你没有登录帐号的话可以到这里查看精华区文章)则写的更为详细:
--------------------------------------------------------------------
关于洗牌问题,其实已经有了一个很好的shell解法,这里另外给三个基于AWK的方法,有错误之处还请不吝指出。
方法一:穷举
类似于穷举法,构造一个散列来记录已经打印行出现行的次数,如果出现次数多于一次则不进行处理,这样可以防止重复,但缺点是加大了系统的开销。
awk-vN=`sed-n'$='data`' BEGIN{ FS="\n"; 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(xinb) { printb[x]; }}
functionC(arr,len,i,j,t,x){
for(xinarr) { i=int(len*rand())+1; j=int(len*rand())+1; t=arr[i]; arr[i]=arr[j]; arr[j]=t; }
}