Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re: Randomize elements among several arrays, while maintaining original array sizes.

by BrowserUk (Patriarch)
on Aug 01, 2008 at 16:54 UTC ( [id://701750]=note: print w/replies, xml ) Need Help??


in reply to Randomize elements among several arrays, while maintaining original array sizes.

Here's a neat use of Perl's aliasing of @_.

#! perl -slw use strict; ## Shuffle an array in place; sub shuffle { my $ref = @_ == 1 ? $_[ 0 ] : \@_; my $n = @$ref; for( 0 .. $#$ref ) { my $p = $_ + rand( $n-- ); my $t = $ref->[ $p ]; $ref->[ $p ] = $ref->[ $_ ]; $ref->[ $_ ] = $t; } return unless defined wantarray; return wantarray ? @{ $ref } : $ref; } my @a = 'a' .. 'c'; my @b = 1 .. 4; my @c = 'A' .. 'E'; shuffle( @a, @b, @c );; print "A:= [ @a ]\nB:= [ @b ]\nC:= [ @c ]"; __END__ c:\test>junk8 A:= [ c b D ] B:= [ 4 A 3 B ] C:= [ E 2 a 1 C ]

Notice how after the shuffle, the elements of the arrays have been intermixed in the 3 arrays, but each knows how big it is. This is because @_ gets aliased to the elements of all 3 arrays.

Thus, by shuffling @_ within the sub, we are shuffling the elements of all 3 arrays at the same time.


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re^2: Randomize elements among several arrays, while maintaining original array sizes.
by Roy Johnson (Monsignor) on Aug 01, 2008 at 19:07 UTC
    Brilliant!

    Note that you can swap without temp variables:

    for( 0 .. $#$ref ) { my $p = $_ + rand( $n-- ); @{$ref}[$p,$_] = @{$ref}[$_,$p]; }
    You can even use List::Util 'shuffle':
    sub my_shuffle2 { my $ref = \@_; use List::Util 'shuffle'; @{$ref}[0..$#$ref] = shuffle(@$ref); return unless defined wantarray; return wantarray ? @{ $ref } : $ref; }

    Caution: Contents may have been coded under pressure.
Re^2: Randomize elements among several arrays, while maintaining original array sizes.
by BioNrd (Monk) on Aug 01, 2008 at 17:04 UTC
    That will do it. Thanks for all the suggestions. This has helped a great deal.

    Thanks again!

    ---- Even a blind squirrel finds a nut sometimes.
      That will do it. Thanks for all the suggestions. This has helped a great deal.

      I personally believe that now that you acknowledge BrowserUk for gaving you an actual solution to your problem, it is eventually clear what the problem really was, which has not been the case for most of us up to this point. Let me try a possibly better phrasing:

      "Take N arrays and shuffle jointly all of their entries, redistribuiting them amongst the same N arrays while leaving their lenghts unchanged."

      In which case, I admit I would have worked with references, and I would have never ever thought of BUk's cool and neat trick: I generally don't write subs that modify stuff that's passed to them in place, but just pass by value and take return values. This, in turn, is most often sensible. But then one shouldn't blindly generalize: occasionally I deviate from that rule adopting the backslashed prototypes. In fact I am not ashamed to confess that I had to stare at BUk's code for some time, to understand how it could achieve a similar semantics without them. Of course it's very trivial instead, but it can easily seem not to be after you've been "reasoning" along different lines for years.

      Now, armed with:

      1. the precise definition of the problem,
      2. a convenient interface courtesy of @_'s aliasing properties,

      let me tell you that you can adoopt just about any shuffling technique you like, including the much recommended "do not reinvent the wheel and use List::Util's shuffle()" one:

      #!/usr/bin/perl use strict; use warnings; use List::Util 'shuffle'; sub shuffit { @_[0..$#_] = shuffle @_; } my @a = 'a' .. 'c'; my @b = 1 .. 4; my @c = 'A' .. 'E'; shuffit @a, @b, @c; print "A:= [ @a ]\nB:= [ @b ]\nC:= [ @c ]\n"; __END__ C:\temp>buk.pl A:= [ 2 D C ] B:= [ a 1 b 4 ] C:= [ 3 B c E A ]
      To complete the meditation above: while this remains a kind of interface that I'm not likely to resort to on a daily basis, I realize that it's both perfectly well suited for this particular case and for many other ones I may stumble upon. Which is the reason why I will always thank you for having brought up the question, even if the question itself is more of an aside in this respect.

      --
      If you can't understand the incipit, then please check the IPB Campaign.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://701750]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2024-04-20 01:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found