【发布时间】:2011-02-22 15:10:55
【问题描述】:
在 Perl 中从哈希中获取具有最高值的键的最简单方法是什么?
【问题讨论】:
在 Perl 中从哈希中获取具有最高值的键的最简单方法是什么?
【问题讨论】:
虽然用排序的解决方案:
(sort {$hash{$a} <=> $hash{$b}} keys %hash)[0]
在其他一些答案中发现非常优雅,但它的性能不如看起来那么好。首先,排序将O(n) 搜索操作转换为O(n log n) 操作。其次,排序解决方案具有n log n 哈希查找。哈希查找对于某些操作非常有用,但是在处理整个哈希时,查找将比使用each、keys 或values 来遍历数据结构要慢。这是因为迭代器不需要计算键的哈希值,也不需要重复遍历 bin 来查找值。并且开销不是恒定的,而是随着哈希值变大而增加。
这里有一些更快的解决方案:
use strict;
use warnings;
my %hash = (
small => 1,
medium => 5,
largest => 10,
large => 8,
tiny => 0.1,
);
这是使用each 迭代器的解决方案(O(1) 操作完成n 次):
sub largest_value (\%) {
my $hash = shift;
keys %$hash; # reset the each iterator
my ($large_key, $large_val) = each %$hash;
while (my ($key, $val) = each %$hash) {
if ($val > $large_val) {
$large_val = $val;
$large_key = $key;
}
}
$large_key
}
print largest_value %hash; # prints 'largest'
或者以内存换取速度的更快版本(它会复制哈希):
sub largest_value_mem (\%) {
my $hash = shift;
my ($key, @keys) = keys %$hash;
my ($big, @vals) = values %$hash;
for (0 .. $#keys) {
if ($vals[$_] > $big) {
$big = $vals[$_];
$key = $keys[$_];
}
}
$key
}
print largest_value_mem %hash; # prints 'largest'
以下是各种哈希大小的性能:
10 keys: Rate largest_with_sort largest_value largest_value_mem
largest_with_sort 111565/s -- -8% -13%
largest_value 121743/s 9% -- -5%
largest_value_mem 127783/s 15% 5% --
50 keys: Rate largest_with_sort largest_value largest_value_mem
largest_with_sort 24912/s -- -37% -40%
largest_value 39361/s 58% -- -6%
largest_value_mem 41810/s 68% 6% --
100 keys: Rate largest_with_sort largest_value largest_value_mem
largest_with_sort 9894/s -- -50% -56%
largest_value 19680/s 99% -- -12%
largest_value_mem 22371/s 126% 14% --
1,000 keys: Rate largest_with_sort largest_value largest_value_mem
largest_with_sort 668/s -- -69% -71%
largest_value 2183/s 227% -- -7%
largest_value_mem 2341/s 250% 7% --
10,000 keys: Rate largest_with_sort largest_value largest_value_mem
largest_with_sort 46.5/s -- -79% -81%
largest_value 216/s 365% -- -11%
largest_value_mem 242/s 421% 12% --
如您所见,如果内存不是什么大问题,则具有内部数组的版本最快,紧随其后的是 each 迭代器,排在第三位……sort
【讨论】:
不知道为什么每个人都手工做这个......
use List::Util qw( reduce );
my $max_val_key = reduce { $hash{$a} > $hash{$b} ? $a : $b } keys %hash;
【讨论】:
与对哈希进行排序的其他答案相比,以下内容更节省空间,并且将以 O(n) 而不是 O(n log n) 运行。它假定值是大于 0 的整数,并且哈希不为空,但应该很容易针对您的情况进行扩展。
my $key_for_max_value;
my $max_value = -1;
while ((my $key, my $value) = each %hash) {
if ($value > $max_value) {
$max_value = $value;
$max_key = $key;
}
}
$key_for_max_value 现在将是对应于最大值的键。
【讨论】:
sort 的解决方案慢。
按值排序的键,从低到高:
sort { $hash{$a} <=> $hash{$b} } keys %hash
按值排序的键,从高到低:
reverse sort { $hash{$a} <=> $hash{$b} } keys %hash
还有第一个元素
(reverse sort { $hash{$a} <=> $hash{$b} } keys %hash)[0]
把宇宙飞船换成cmp来尝尝。
【讨论】:
values 而不是keys?
$hash{$b} <=> $hash{$a} 而不是reverse
my ($max_key, $max_val) = each %hash or die "hash is empty";
while (my ($key, $val) = each %hash) {
$max_key = $key, $max_val = $val if $val > $max_val;
}
【讨论】:
my $highest_val = (sort { $hash{$a} <=> $hash{$b} } keys %hash)[0];
很可能是你想要的。
如果你有一个非常大的散列,你可能想要使用类似 Schwartzian 变换的东西:
my @array = map {[$hash{$_},$_]} keys %hash;
my $key_with_highest_value = (sort { $a->[0] <=> $b->[0] } @array)[0]->[1]
【讨论】:
my $highest_val = (keys {$hash{$b} <=> $hash{$a}} keys %hash)[0];
【讨论】:
And in that case, why not just "reverse sort keys %hash"? - 因为这是一种词汇排序,而sort {$b <=> $a} 用一块石头击中了两只鸟,因为它既是数字排序又是相反的。
如果性能不是问题,我会建议更多 literate programming 解决方案。
use List::Util qw(max);
max keys %hash;
【讨论】: