配列をランダムかき混ぜる


PerlFAQ(CPAN以下の/doc/FAQs/FAQ/PerlFAQ.htmlにあります。)
より抜粋。

---------------- FAQ ここから ---------------------
[How do I shuffle an array randomly?]
> [配列をランダムに混ぜるには?]

Here's a shuffling algorithm which works its way through
the list, randomly picking another element to swap the
current element with: 
> 以下が、質問と同じ動作をするアルゴリズムです。
> 新しい配列に追加するためにランダムに要素を拾っています。

    srand;
    @new = ();
    @old = 1 .. 10;  # just a demo
    while (@old) {
		push(@new, splice(@old, rand @old, 1));
    }

For large arrays, this avoids a lot of the reshuffling: 
> 大きな配列の場合。大量の並替えを防ぎます。

    srand;
    @new = ();
    @old = 1 .. 10000;  # just a demo
    for( @old ){
        my $r = rand @new+1;
        push(@new,$new[$r]);
        $new[$r] = $_;
    }

> 灰色部かなだ訳。かなり意訳。
---------------- FAQ ここまで ---------------------
配列がでかい場合には、2番目の方法が効率が良いでしょう。
spliceを使わないし、元の配列を壊しません。
またファイルから読んで、行ごとにランダムに混ぜたい場合
はfor(@old)の代わりにwhile(<FILE>)とし、

    srand;
    @new = ();
	open(FILE, "混ぜたいファイル");
    for(<FILE>){
        my $r = rand @new+1;
        push(@new,$new[$r]);
        $new[$r] = $_;
    }
	close(FILE);

のようにやれば配列・メモリの無駄遣いをせずに済みます。

#以下余談

配列からランダムに1つ取り出したい場合は単に

    srand;
    print $foo[rand @foo];

の様にすれば良いでしょう。

ファイルから、ランダムに1行「だけ」取り出したい場合は、
同FAQの「How do I select a random line from a file?」
や赤ラクダ本の316pの例にあるように

srand;
open(FILE, "filename");
rand($.) < 1 && ($it = $_) while <FILE>;
close(FILE);
print $it;

としてやれば良いと思います。