Chinaunix

标题: 按列倒序排列 [打印本页]

作者: yestreenstars    时间: 2013-04-18 10:55
标题: 按列倒序排列
从某群搬运过来的一道题:

处理前:
85|44|21|75
12|52|45|78
04|85|15|32

处理后:
85|85|45|78
12|52|21|75
04|44|15|32

要求:
1.对每一列进行排序(从大到小)
2.代码尽量不要有局限性,应具有延展性(比如增加多列或者增加多行时也应有效)
3.最好不借助管道用awk完成

我目前只想到一个,但是具有局限性,无论是增加还是减少列都要重新修改代码,所以看看大家有没有什么好方法~
下面是我的代码,为了不影响各位的思路,我先把代码隐藏起来~

作者: wenhq    时间: 2013-04-18 11:27
看下LZ的代码。
作者: wenhq    时间: 2013-04-18 11:37
思路说下,每一行的值求和肯定比下一行的值大。这样第一行的所有元素应该是该列中最大的。然后循环。这样就成了。


作者: linuxforlive    时间: 2013-04-18 11:47
进来学习的
作者: yestreenstars    时间: 2013-04-18 12:07
回复 3# wenhq


    不能纸上谈兵,要以实际行动证明~
作者: yinyuemi    时间: 2013-04-18 12:31
回复 1# yestreenstars


    gawk 4.0
  1. echo '115|85|45|78
  2. 12|52|21|75
  3. 04|44|15|32' |awk -F\| '{for(i=1;i<=NF;i++){a[i][NR]=$i}}END{for(i in a)for(j=1;j<=asorti(a[i],b);j++)c[b[j]][i]=a[i][b[j]];for(i=1;i<=NR;i++){for(j=1;j<=NF;j++){printf c[i][j] FS};print ""}}'
  4. 115|85|45|78|
  5. 12|52|21|75|
  6. 04|44|15|32|
复制代码

作者: blackold    时间: 2013-04-18 12:37
回复 6# yinyuemi


    学习!
作者: blackold    时间: 2013-04-18 12:38
awk 4 支持数值排序,强。
作者: yinyuemi    时间: 2013-04-18 12:45
回复 7# blackold


    黑哥好久不见~
    gawk 4.0 的确有很多fancy的新东西,我超喜欢这个array of array
作者: blackold    时间: 2013-04-18 12:47
回复 9# yinyuemi


    想你了^_^
作者: yestreenstars    时间: 2013-04-18 13:13
回复 6# yinyuemi


    为啥我用yum update gawk也更新不到4.0的版本~ 估计这种方法转化不到4.0以下的版本可用吧?
作者: yestreenstars    时间: 2013-04-18 13:15
回复 8# blackold


    黑哥,来个4.0以下能用的吧~
作者: blackold    时间: 2013-04-18 13:21
回复 12# yestreenstars


    4以下要自己写个排序函数才行。

作者: yinyuemi    时间: 2013-04-18 13:21
本帖最后由 yinyuemi 于 2013-04-18 13:23 编辑

回复 11# yestreenstars
  1. ---
复制代码

作者: cjaizss    时间: 2013-04-18 13:26
可以做出来的,
每一行的列数一致吗?
作者: yestreenstars    时间: 2013-04-18 13:27
回复 15# cjaizss


    一致~
作者: cjaizss    时间: 2013-04-18 13:28
yestreenstars 发表于 2013-04-18 13:27
回复 15# cjaizss

哦,一致的话,那更加容易一些,我试着写写看,
以前类似的东西用awk处理过列数不一致的
作者: Shell_HAT    时间: 2013-04-18 13:33
回复 12# yestreenstars


    http://ftp.gnu.org/gnu/gawk/
作者: yestreenstars    时间: 2013-04-18 13:40
回复 18# Shell_HAT


    多谢版主~不过我一直只会用傻瓜式的rpm安装和yum安装,对于源码包安装一直不知道怎么弄~ 我去百度查查~
作者: cjaizss    时间: 2013-04-18 13:41

  1. awk 'BEGIN{FS=OFS="|"}{for(i=1;i<=NF;i++)a[NR,i]=$i}END{for(i=1;i<=NF;i++){for(j=1;j<=NR;j++)b[j]=a[j,i];asort(b,c);for(j=1;j<=NR;j++)d[j,i]=c[NR+1-j];delete b;delete c;}for(j=1;j<=NR;j++){for(i=1;i<=NF-1;i++)printf("%s|",d[j,i]);printf("%s\n",d[j,NF]);}}'
复制代码

作者: cjaizss    时间: 2013-04-18 13:45
yinyuemi 发表于 2013-04-18 12:45
回复 7# blackold

有这个功能的话,那awk就真的太强大了,编写的时候可以方便很多
可以举个例子吗?
作者: ly5066113    时间: 2013-04-18 13:53
写了个不借助 asort 的版本
  1. awk -F \| '{for(i=1;i<=NF;i++){for(j=1;j<NR;j++){if($i>a[i","j])break}for(k=NR;k>j;k--)a[i","k]=a[i","(k-1)];a[i","j]=$i}m=NF;n=NR}END{for(i=1;i<=n;i++){for(j=1;j<m;j++)printf a[j","i]"|";print a[j","i]}}' file
复制代码

作者: yinyuemi    时间: 2013-04-18 13:55
回复 21# cjaizss


老大,我以前发过个帖子,gawk4.0 的新功能~
   
http://bbs.chinaunix.net/thread-3559813-1-1.html



作者: wenhq    时间: 2013-04-18 13:55
回复 5# yestreenstars


    恩,我说的是思路。要写出来就直接贴代码了。
作者: yestreenstars    时间: 2013-04-18 14:05
本帖最后由 yestreenstars 于 2013-04-18 14:36 编辑

回复 20# cjaizss


    太好了~ 看了你的代码的前半部分,再结合我之前自己想的半成品,我也写了一个:
  1. awk -F\| '{for(i=1;i<=NF;i++)a[i]=a[i]!=""?a[i]" "$i:$i}END{for(i=1;i<=NF;i++){split(a[i],b," ");asort(b,c);for(j=1;j<=NR;j++)d[i,j]=c[j]}for(i=NR;i>=1;i--){for(j=1;j<NF;j++)printf "%s|",d[j,i];print d[NF,i]}}'
复制代码

作者: cjaizss    时间: 2013-04-18 14:14
yinyuemi 发表于 2013-04-18 13:55
回复 21# cjaizss

收到,拜读先
作者: yestreenstars    时间: 2013-04-18 14:17
回复 23# yinyuemi


    学习了~
作者: seesea2517    时间: 2013-04-18 14:19
不用 awk 应该就没有管道的限制了吧,哈哈。
  1. [seesea@UC ~]$ file_name="file"; delimiter='|'; cmd="paste -d'$delimiter'"; line=$(head -1 $file_name); line=${line//[^$delimiter]}; for ((i = 1; i <= ${#line}+1; ++i)); do cmd="$cmd <(cut -d'$delimiter' -f$i $file_name | sort -rn)"; done; eval "$cmd";
  2. 85|85|45|78
  3. 12|52|21|75
  4. 04|44|15|32
复制代码

作者: yestreenstars    时间: 2013-04-18 14:22
回复 28# seesea2517


    哈哈,你好喜欢用shell,不过我的原意是不借助管道,使用awk来解决~
作者: cjaizss    时间: 2013-04-18 14:38
seesea2517 发表于 2013-04-18 14:19
不用 awk 应该就没有管道的限制了吧,哈哈。

挺好,不过我一般记不住paste啊,sort啊,cut,tr什么的命令,记得awk/sed搞定,呵呵
作者: 刺客阿地    时间: 2013-04-18 14:54
现在功力未到,只能看看先,还不懂其中的很多写法意思!
作者: seesea2517    时间: 2013-04-18 15:21
回复 29# yestreenstars


    这不是说“最好”嘛。再说了,你这限制awk不把人的思维给限制住啦。当然啦,从效率以及适合度来说,awk应该是首选。
作者: seesea2517    时间: 2013-04-18 15:23
回复 30# cjaizss


    嗯,awk看起来已经万能了。其它的小工具只是特定功能。对于简单功能自然使用特定工具来得简单,不过复杂一些的功能要搭配小工具,各种管道倒来倒去,效率好不了。
作者: yestreenstars    时间: 2013-04-18 16:28
回复 22# ly5066113


    这个思路有点难以理解,我稍微改动了一下(精简):
  1. awk -F\| '{for(i=1;i<=NF;i++){for(j=1;j<NR;j++)if($i>a[i,j])break;for(k=NR;k>j;k--)a[i,k]=a[i,(k-1)];a[i,j]=$i}}END{for(i=1;i<=NR;i++){for(j=1;j<NF;j++)printf a[j,i]"|";print a[j,i]}}'
复制代码

作者: pitonas    时间: 2013-04-18 17:11
awk真的太强大了,学习了~
作者: wenhq    时间: 2013-04-23 17:23
回复 22# ly5066113


Tim大师能解释下吗?有点不理解。



   
作者: ly5066113    时间: 2013-04-23 18:03
回复 36# wenhq


简单点说,就是把所有数据都记录到数组中,用 1,1  2,3 这样的字串当数组的下标,来记录位置。
每读一行记录,循环每一个数字,找到该数字在数组中所在那一列的位置(数组中第一个比它小的元素的位置),记录位置,下面的所有数字依次向下移位,然后把这个数字插入到这个位置,如果没有找到,就插入到最后面。

以第一列为例:
第一行: a[1,1]=85
第二行:12 比 所有元素都小,找不到位置,所以插入最后 a[1,1]=85  a[1,2]=12
第二行:04 比 所有元素都小,找不到位置,所以插入最后 a[1,1]=85  a[1,2]=12  a[1,3]=04

以第二列为例:
第一行: a[2,1]=44
第二行:52 比 44大,44及后面的所有元素向下移位 a[2,2]=44 ,52插入到原44的位置 a[2,1]=52
第三行:85 比 52大,52及后面的所有元素向下移位 a[2,2]=52  a[2,3]=44 ,85插入到原52的位置 a[2,1]=85

以此类推。
作者: wenhq    时间: 2013-04-24 09:40
回复 37# ly5066113

那a[1,1]=85 是在什么部分体现的呢?怎么得出来的呢?

思路我大体上明白了。就是到 if( $i>a[i",“j] )  这里卡住了,因为a[i","j]没初始化? 或者说初始化了,在那里体现了?

   
作者: ly5066113    时间: 2013-04-24 10:29
本帖最后由 ly5066113 于 2013-04-24 10:29 编辑

回复 38# wenhq


仔细看循环的条件,第一行时 for(j=1;j<NR;j++) 和 for(k=NR;k>j;k--) 循环里面是都不会执行的

直接执行 a[i","j]=$i
作者: wenhq    时间: 2013-04-24 10:34
回复 39# ly5066113
看到了谢谢Tim大师。

   
作者: seesea2517    时间: 2013-04-24 10:43
回复 37# ly5066113


    看到这么多的“插入”,那就是插入排序喽。
作者: lbbei    时间: 2013-05-29 13:24
偶来学习哈
作者: qjpoo    时间: 2016-08-25 22:24
好东西,学习一下
作者: sunzhiguolu    时间: 2016-08-29 17:49
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;

  4. my (@aData, @aOut);
  5. while (<DATA>){
  6.     chomp;
  7.     my @aList = split (/\|/);
  8.     push (@{$aData[$_]}, $aList[$_]) for 0 .. $#aList;
  9. }

  10. map {$_ = [sort {$b <=> $a} @$_]} @aData;

  11. while (my ($idx, $vals) = each @aData){
  12.     my $n = 0;
  13.     foreach (@$vals){
  14.         push (@{$aOut[$n++]}, $_);
  15.     }
  16. }

  17. print join ("|", @$_), "\n" for @aOut;

  18. __DATA__
  19. 85|44|21|75
  20. 12|52|45|78
  21. 04|85|15|32
复制代码

85|85|45|78
12|52|21|75
04|44|15|32





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2