perl:shuffle value-sorted hash?

时间:2011-11-29 14:50:06

标签: perl sorting hash

首先抱歉我的英语 - 我希望你能理解我。

有一个哈希:

$hash{a} = 1;
$hash{b} = 3;
$hash{c} = 3;
$hash{d} = 2;
$hash{e} = 1;
$hash{f} = 1;

我想按值(而不是键)对它进行排序,所以我有:

for my $key ( sort { $hash{ $a } <=> $hash{ $b } } keys %hash  ) { ... }

首先,我得到值为1的所有键,然后是值2,等等......很棒。

但是如果哈希没有改变,那么键的顺序(按此值排序)总是相同的。

问题:我如何改变排序结果,所以每次运行'for'循环时,我得到不同的键顺序,值为1,值为2等?

5 个答案:

答案 0 :(得分:4)

我不太清楚我是否理解你的需求,但这是好的:

use List::Util qw(shuffle);

my %hash;
$hash{a} = 1;
$hash{b} = 3;
$hash{c} = 3;
$hash{d} = 2;
$hash{e} = 1;
$hash{f} = 1;

for my $key (sort { $hash{ $a } <=> $hash{ $b } } shuffle( keys %hash  )) {
    say "hash{$key} = $hash{$key}"
}

答案 1 :(得分:3)

您可以简单地添加另一级别的排序,当常规排序方法无法区分两个值时,将使用这种排序。 E.g:

sort { METHOD_1 || METHOD_2 || ... METHOD_N } LIST

例如:

sub regular_sort {
    my $hash = shift;
    for (sort { $hash->{$a} <=> $hash->{$b} } keys %$hash) {
        print "$_ ";
    };
}
sub random_sort {
    my $hash = shift;
    my %rand = map { $_ => rand } keys %hash;
    for (sort { $hash->{$a} <=> $hash->{$b} ||
        $rand{$a} <=> $rand{$b} } keys %$hash ) {
        print "$_ ";
    };
}

答案 2 :(得分:2)

要按值对键进行排序,使用具有相同值的键的随机排序,我会看到两个解决方案:

use List::Util qw( shuffle );
use sort 'stable';
my @keys =
   sort { $hash{$a} <=> $hash{$b} }
   shuffle keys %hash;

my @keys =
   map $_->[0],
   sort { $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
   map [ $_, $hash{$_}, rand ],
   keys %hash;

要求use sort 'stable';阻止sort破坏shuffle返回的列表的随机性。


以上对Schwartzian Transform的使用不是优化尝试。我看到人们在比较函数本身中使用rand来尝试实现上述结果,但这样做有两个原因。

当使用诸如此类的“行为不当”比较时,结果被记录为 undefined ,因此允许sort返回垃圾,重复元素,缺少元素等。

即使sort没有返回垃圾,也不会是合理的。结果将被称重。

答案 3 :(得分:1)

您可以使用升序和降序两个函数,并相应地使用它们,如

sub hasAscending {
   $hash{$a} <=> $hash{$b};
}

sub hashDescending {
   $hash{$b} <=> $hash{$a};
}

foreach $key (sort hashAscending (keys(%hash))) {
   print "\t$hash{$key} \t\t $key\n";
}

foreach $key (sort hashDescending (keys(%hash))) {
   print "\t$hash{$key} \t\t $key\n";
}

答案 4 :(得分:1)

好像你想随机化循环键。

Perl,不按顺序或排序顺序存储,但这对你来说似乎不够随意,所以你可能想要创建一个键数组并循环遍历它。

首先,使用键填充数组,然后使用随机数算法(1 .. $ #length_of_array)将数组中该位置的键推送到array_of_keys。


如果你试图随机化按值排序哈希的键,那就有点不同了。

See Codepad

my %hash = (a=>1, b=>3, c=>3, d=>2, e=>1, f=>1);
my %hash_by_val;

for my $key ( sort { $hash{$a} <=> $hash{$b} } keys %hash ) { 
   push @{ $hash_by_val{$hash{$key}} }, $key;
}


for my $key (sort keys %hash_by_val){
   my @arr        = @{$hash_by_val{$key}};
   my $arr_ubound = $#arr;

   for (0..$arr_ubound){
      my $randnum = int(rand($arr_ubound));
      my $val     = splice(@arr,$randnum,1);
      $arr_ubound--;
      print "$key : $val\n";                    # notice: output varies b/t runs
   }
}