use warnings; use strict; use Benchmark ":all"; use IO::Handle; my %shuffle; use List::Util; $shuffle{"xs"} = \&List::Util::shuffle; $shuffle{"swap"} = sub { my @p = @_; my $k; $k = $_ + int(rand(@p - $_)), @p[$_, $k] = @p[$k, $_] for 0 .. @p - 1; @p; }; $shuffle{"map"} = sub { my @p = @_; map { splice @p, $_ + rand(@p - $_), 1, $p[$_] } 0 .. @p - 1; }; $shuffle{"shift"} = sub { my @p = @_; my $t; map { $t = splice @p, rand(@p), 1, $p[0]; shift @p; $t } 0 .. @p - 1; }; $shuffle{"weight"} = sub { my @w = map { rand } @_; @_[sort { $w[$a] <=> $w[$b] } 0 .. @_ - 1]; }; $shuffle{"schwartzian"} = sub { map { $$_[1] } sort { $$a[0] <=> $$b[0] } map { [rand, $_] } @_; }; if (0) { $shuffle{"bless"} = sub { map { $$_[0] } sort { ref $a <=> ref $b } map { bless [$_], rand } @_; }; } if (0) { my $N = 0 + ($ARGV[0] || 20); my $T = 0 + ($ARGV[1] || 1000); for my $n (keys(%shuffle)) { my %f; print $n; my $a = $shuffle{$n}; for (1 .. $T) { my @a = 1 .. $N; $f{join(" ", &$a(@a))}++; } for (sort keys(%f)) { printf "%5d: %s\n", $f{$_}, $_; } print 0+keys(%f), "\n\n"; } } my @sd; my %shtest = map { my($n, $a) = ($_, $shuffle{$_}); $n, sub { my @r = &$a(@sd); } } keys(%shuffle); for my $N (map { 1 + 2 * 3**$_ } 0 .. 8) { print "testing on $N elements\n"; @sd = my @sd0 = map { rand } 1 .. $N; cmpthese(-5, \%shtest); join(";", @sd) eq join(";", @sd0) or die "error: some of the shuffle functions have changed the array"; flush STDOUT; } print "done\n"; __END__