Hatena::Groupperl

local $PERL_MEMO;

March 04, 2010

[][][]配列をshuffle(ランダムに並べ替えたい)/take potluck(ランダムに抜き出したい) はてなブックマーク - 配列をshuffle(ランダムに並べ替えたい)/take potluck(ランダムに抜き出したい) - local $PERL_MEMO; 配列をshuffle(ランダムに並べ替えたい)/take potluck(ランダムに抜き出したい) - local $PERL_MEMO; のブックマークコメント

たまーに配列を適当に並べ替えたり、そこからいくつか抜き出したいってことがあります。そんなときのためのTIP。

並べ替え

お手軽に
sub shuffle {
  return sort { int(rand 3) - 1 } @_
}
print shuffle(0 .. 9);
# 0123456789 => 6517840923

非常に簡単です。ただ元の配列の並びの影響を受けやすいので、内容が1020個程度の配列なら気にならないですが、それ以上になるとあまり役に立ちません。

きちんと
sub shuffle {
  my @old = @_; local $_;
  my ($i, $new) = ($#old+1, 0);
  map {
    $_ = $old[$new = rand $i--];
    $old[$new] = $old[$i];
    $_;
  } 0 .. $#old
}
print shuffle(0..9,A..Z,a..z);
# before: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
# after : qBwNj3uiMgtLG9TKs6Ry7fpmZvVUSDQOdEeaJWoX8nlr5Cc0h2xbH4FIAPkYz1

ややこしいですがどんだけ大きな配列を入れようがランダムです。

適当に抜き出す

お手軽に
sub shuffle {
  return sort { int(rand 3) - 1 } @_
}
print ((shuffle(0..9,A..Z,a..z))[0..9]);
# iOKq7r1GL8

適当に並べ替えた奴の先頭から10個取ってるだけですね。簡単簡単。でも低ランダム。(元の配列の頭のほうばっかり取ってきます)

元の配列が抜き出したい数だけあるか分からないとき(上の例なら10個あるか分からないとき)は undef 抜くために

print grep { defined } ((shuffle(0..rand 9))[0..9]);

とかしましょう。元がundef入りの配列だと厄介だが。

かっつり
sub potluck {
  my @old = @{+shift}; local $_;
  my ($i, $new) = ($#old+1, 0);
  my $count = shift || int rand $i;
     $count = $i if $count > $i;
  map {
    $_ = $old[$new = rand $i--];
    $old[$new] = $old[$i];
    $_;
  } 0 .. $count-1
}
print potluck([0..9,A..Z,a..z]);
# hx9Py6m83ERG5JfarLqMeBwQozuZCYUv4i02KbntTDdpjs7NHgXO1WAI
print potluck([0..9,A..Z,a..z],10);
# o5fjvETbBJ

さっきのsub shuffleをちょっといじっただけです。配列のリファレンスを渡します。第2引数に抜き出したい数を渡すとその分だけ取ってきます。元の配列の大きさは超えません。

番外: List::Utilを使う

use List::Util qw(shuffle);
print shuffle(0..9,A..Z,a..z);
# 81D9moCj3n0zPO4VwIpGu5aFJR2ftTlyEgvxMHebhcXZYkqUiB6SWdAKrQNs7L

List::Utilはuseしただけでは関数をエクスポートしてくれないので使いたい関数は必ずインポートしましょう。

List::Utilはこれ以上ないくらい洗練されてる(と思う)のでv5.7.3以降のperlを使ってる人はこれ使うといいですね。

抜き出すのはないっぽいから自前で何とかしよう!

追記

  • 「お手軽に抜き出す」に追記。あと0..1011個ですね…
  • 丸め込みのこととかいろいろ考慮してなくて真面目なsub shuffle/potluckがおかしなこと*1になってたので修正しました

さらに追記

*1$old[rand $#old]だと配列の末尾が取れないとか、potluck(\@hoge,10)11個返すとか

NyannaNyanna2011/06/05 09:47Home run! Great slgungig with that answer!

yikfqnoyikfqno2011/06/05 18:02NgKXa8 <a href="http://ytwaiwllkaqd.com/">ytwaiwllkaqd</a>

LorrenLorren2011/06/05 21:19Your answer was just what I nedeed. It’s made my day!

mpusalcmpusalc2011/06/06 23:07fZNUyi , [url=http://fxmxjhkixatn.com/]fxmxjhkixatn[/url], [link=http://xlzdgbhgjqsh.com/]xlzdgbhgjqsh[/link], http://xffxfxayqpgo.com/

xicfqdquxicfqdqu2011/06/07 18:12VSQMgx <a href="http://ohsmkfuuaqow.com/">ohsmkfuuaqow</a>

hgucshczgplhgucshczgpl2011/06/09 19:27zzfuU2 , [url=http://uiqorlryztty.com/]uiqorlryztty[/url], [link=http://keydymymivxx.com/]keydymymivxx[/link], http://ctmxknborxko.com/