2011-11-29 84 views
2

起初對不起我的英語 - 我希望你能理解我。perl:shuffle value-sorted 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等的鍵的順序?

+1

你想對它排序,但是對於具有相同值的鍵具有隨機順序,對嗎? –

+0

我只在for循環中「排序」之前嘗試過洗牌(來自List :: Util),但顯然沒有工作,因爲它洗牌整個哈希..喬納森:是的,這是正確的。 – gib

+0

等待,您是否想在按值排序之前隨機化返回的鍵的順序(排序後,仍然保持按值排序)或鍵的順序? – vol7ron

回答

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}" 
} 
+0

這和多級排序(由TLP)的例子:)工作。多謝你們! – gib

+0

@gibson不客氣。 – TLP

+0

@ M42,如果你想要一個合理的排序,你需要使用'use sort'stable';'。如果沒有它,'sort'可能會破壞'shuffle'的結果。 – ikegami

1

你可以有升序和decending爲了兩個函數和像你想通過隨機按鍵循環相​​應地使用他們喜歡

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"; 
} 
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 
    } 
} 
+0

重新您的第一段:這不是設計。這是散列的副作用。雖然訂單不知道,但它也不是隨機的。它甚至可以預測。例如,運行'perl -E'%h = map {$ _ => 1} qw(a b c d);說鍵%h;''幾次。 – ikegami

+0

@ikegami:你的話是金子,所以我不懷疑你,但是不是爲了減少尋找時間而設計的嗎? – vol7ron

+0

如果這樣做是爲了減少散列表的查找時間,這意味着可以有一個有序的散列表,代價是增加散列表的查找時間(不管是什麼)。但是不可能有一個有序的哈希表,所以沒有做到減少哈希表的查找時間。同樣,這只是使用[哈希表](http://en.wikipedia.org/wiki/Hash_table)的副作用。 – ikegami

3

你可以簡單地添加排序的另一個層面,這將被用來當常規排序方法無法區分兩個值。例如:

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 "$_ "; 
    }; 
} 
+0

您的代碼有兩個原因。 1)當使用諸如此類的「行爲不當」比較時,結果記錄爲* undefined *,因此'sort'允許返回垃圾,重複元素,缺少元素等。2)即使'sort'沒有返回垃圾,這不會是一個公平的排序。結果將被稱重。我發佈修復作爲答案。 – ikegami

+0

@ikegami我沒有看到數字比較或在[documentation](http://perldoc.perl.org/functions/sort.html)中使用導致未定義結果的函數。請提供一些關於這些陳述的文件或解釋。 – TLP

+0

它在底部。 「比較函數需要表現出來,如果返回不一致的結果(有時候說'$ x [1]'小於'$ x [2]',有時會說相反的結果),結果沒有明確定義「。 – ikegami

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來試圖達到上述結果,但這樣做是有問題的,原因有兩個。

當使用「行爲不端」的比較,諸如,將結果記錄爲未定義,所以sort被允許返回垃圾,重複元素,缺少元素等

即使sort不返回垃圾,這不會是一個合理的排序。結果將被稱重。

+0

+1:直到今天,我還不知道有'sort'雜注:) – Zaid