Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Randomize word

by vikee (Sexton)
on Sep 07, 2004 at 12:39 UTC ( [id://389006]=perlquestion: print w/replies, xml ) Need Help??

vikee has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I need to randomize a word, could anybody help me?

for example the word is "hotel" and I need something that
randomizes that word. ( "htleo", "otelh", "tlohe",...)

Thx

Replies are listed 'Best First'.
Re: Randomize word
by hv (Prior) on Sep 07, 2004 at 12:44 UTC

    Here's one simple approach:

    use List::Util qw/ shuffle /; my $word = 'hotel'; my $random = join '', shuffle split //, $word; print "$random\n";

    Hugo

Re: Randomize word
by tachyon (Chancellor) on Sep 07, 2004 at 12:45 UTC

    The process you describe is a shuffle. If you don't want to use List::Util which AFAIK is not core (for <5.8 ) then you could just use a Fisher Yates shuffle like this:

    sub random { my @array = split //, shift; for (my $i = @array; --$i; ) { my $j = int rand ($i+1); @array[$i,$j] = @array[$j,$i]; } return join '', @array; } print random('hello');

    UpdateAristotle notes that List::Util is core from 5.8.

    cheers

    tachyon

      If you're going to roll your own shuffle, there's no reason you need to work on an array:
      sub rand2 { my $foo = shift; for (my $i = length($foo); --$i; ) { my $j = int rand ($i+1); (substr($foo, $i, 1), substr($foo, $j, 1)) = (substr($foo, $j, + 1), substr($foo, $i, 1)) } return $foo; }
      But then, there's no strong argument for shuffling in-place, either:
      sub rand3 { my $foo = shift; my $bar = ''; while (length $foo) { $bar .= substr($foo, rand(length $foo), 1, ''); } return $bar; }
      Benchmark results:
      Rate rand1 rand2 rand3 rand1 2928/s -- -27% -78% rand2 4008/s 37% -- -70% rand3 13282/s 354% 231% --
      Update: Benchmarked BrowserUK's shuffleword; it was about half as fast as rand3.

      Caution: Contents may have been coded under pressure.

      List::Util is core as of 5.8.

      Makeshifts last the longest.

Re: Randomize word
by BrowserUk (Patriarch) on Sep 07, 2004 at 13:56 UTC

    Why expend all those cycles breaking the string into and array and then more cycles to stick'em all back together again :) (It's even fair!)

    #! perl -slw use strict; sub shuffleWord { my( $l, $p ) = length( $_[0] )-1; $p = int rand $l - $_ and substr( $_[ 0 ], $_ , 1 ) ^= substr( $_[ 0 ], $_ + $p, 1 ) ^= substr( $_[ 0 ], $_ , 1 ) ^= substr( $_[ 0 ], $_ + $p, 1 ) for 0 .. $l; return $_[ 0 ]; } print shuffleWord $ARGV[ 0 ]; __END__ P:\test>test.pl antidisastablishmentarismmania teinisasdrslaathiaismmtabnnima P:\test>test.pl antidisastablishmentarismmania ibsrniitmsaaadnnaealttiimsmhsa P:\test>test.pl antidisastablishmentarismmania iiarsshaminaaittlmtnbasmsednia

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      Last character never get changed

        Remove the -1:

        sub shuffleWord { my( $l, $p ) = length( $_[0] ); $p = int rand $l - $_ and substr( $_[ 0 ], $_ , 1 ) ^= substr( $_[ 0 ], $_ + $p, 1 ) ^= substr( $_[ 0 ], $_ , 1 ) ^= substr( $_[ 0 ], $_ + $p, 1 ) for 0 .. $l; return $_[ 0 ]; }

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        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.
Re: Randomize word
by borisz (Canon) on Sep 07, 2004 at 12:51 UTC
    use List::Util qw/shuffle/; my @w = split //, 'hotel'; for ( 1 .. 10 ) { print shuffle(@w), "\n"; }
    Boris
Re: Randomize word
by Roy Johnson (Monsignor) on Sep 07, 2004 at 14:08 UTC
    With a sufficiently recent Perl, you can let a hash randomize for you:
    $_='hotel'; %f=map {$_=>1} split //; print join('',keys %f), "\n";
    If your Perl isn't sufficiently recent, it will yield the same result every run. Note: this is just fun-hack solution, not a real, recommended one.

    Update: I cannot imagine how I didn't notice what Anonymous Monk did. Make that:

    $_='rotohotel'; my $len=0; my %f=map {$len++ => $_} split //; print join('', values %f), "\n";

    Caution: Contents may have been coded under pressure.
      That wouldn't work if the word to shuffle contains duplicate letters.

        Look again. Duplicate letters work.

        However, ActivePerl v5.6.1 build 633 actually returns unshuffled results for "rotohotel", "r1t2h3tel" and "r1t2h3tels"!! Nothing guarantees that order is lost in a hash.

        May I recommend a variation:

        $_='rotohotel'; my $len; my %f=map {rand() . '|' . $len++ => $_} split //; print join('', values %f), "\n";

        It seems values() is implemented as map { $hash{$_} } sort keys %hash) on this build!!

        Update: ok, so it's not always sorted:

        %hash = map { $_ => $_ } qw(m i f e z l x v b r d o h y j q s t k n g +c a p w u); print(join('', values(%hash)), "\n"); # prints: abcdefghijklmnopqrstuvwxyz SORTED %hash = map { $_ => $_ } qw(m i f e z l x 2 v b r d o h y j q s t k n +g c a p w u); print(join('', values(%hash)), "\n"); # prints: abcdefghijklmnop2qrstuvwxyz NOT SORTED %hash = map { $_ => $_ } qw(6 3 5 0 1 2 9 8 7 4); print(join('', values(%hash)), "\n"); # prints: 0123456789 SORTED %hash = map { $_ => $_ } qw(6 3 5 0 34 1 2 33 9 8 7 4); print(join(':', values(%hash)), "\n"); # prints: 0:1:2:3:4:5:6:7:8:9:33:34 SORTED %hash = map { $_ => $_ } qw(a aaaaaaa aaaa aaa aa aaaaa aaaaaaaaaa); print(join(':', values(%hash)), "\n"); # prints: aa:aaaaaaa:a:aaaaaaaaaa:aaaaa:aaaa:aaa NOT SORTED
Re: Randomize word
by Nkuvu (Priest) on Sep 07, 2004 at 12:57 UTC
    In the spirit of TIMTOWTDI (mostly because I'm not sure if List::Util is in the core distribution), I offer this snippet of code:

    #!/usr/bin/perl use strict; my @word_array = split '', "iguana"; fisher_yates_shuffle(\@word_array); print "Word is ", join ('', @word_array), "\n"; # From perldoc -q shuffle: sub fisher_yates_shuffle { my $deck = shift; # $deck is a reference to an array my $i = @$deck; while ($i--) { my $j = int rand ($i+1); @$deck[$i,$j] = @$deck[$j,$i]; } }

    I would like to know if there's a simple way to remove the extraneous @word_array variable. Maybe I'm just overtired, but I couldn't see a way to get an array reference from the return of the split...

      Your tired and gonna kick yourself :)

      print "Word is: ", join '', @{ fisher_yates_shuffle( [ split '', "iguana" ] ) };

      Of course, you'd have to return the reference passed in though.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
        You are correct on the tired bit. And thanks for the correct syntax -- must have been a mental block on this when I was looking at it earlier.

        I do remember trying bizarre things like @( split '', "iguana") and I'm not sure why I thought that would work. I suppose I should just stick to my original overtired excuse. I was, after all, looking at the sunrise from the wrong side (as in, I was up all night, and went to bed shortly after posting this).

Re: Randomize word
by Anonymous Monk on Sep 07, 2004 at 12:50 UTC
    $ perl -MList::Util=shuffle -wle 'print join "", shuffle split //, shi +ft' hotel othle
Re: Randomize word
by zdog (Priest) on Sep 12, 2004 at 07:17 UTC

    If you only need one randomization, here's a solution I came up with:

    my $word = "whatever"; print join '', sort { rand() <=> rand() } split //, $word;

    Zenon Zabinski | zdog | zdog@perlmonk.org

      Trying:
      $ perl -we'use Data::Dumper; ++$Data::Dumper::Sortkeys; ++$count{join +"", sort \ {rand()<=>rand()}split //, "abcde"} for 1..100000; print Dumper \%coun +t;'|less
      showed a very uneven distribution, though I couldn't make out what the determining factors were. Compare to:
      $ perl -we'use Data::Dumper; ++$Data::Dumper::Sortkeys; use List::Util + "shuffle"; \ ++$count{join "", shuffle split //, "abcde"} for 1..100000; print Dump +er \%count;'|less

        Good looking out: obviously not optimal. I tried to figure out for myself what was going wrong.

        First, I simplified the code itself a little further:

        print join "", sort { rand() <=> 0.5 } split //, "abcde";

        That does basically the same thing, except calls rand() once less per comparison.

        Staring at it didn't do much good for me so I came up with a little test script of my own:

        use strict; use warnings; sub compare { my ($a, $b) = @_; my $rand = rand(); print "$a: $rand; $b: 0.5\n"; return $rand <=> 0.5; } print join "", sort { compare($a, $b) } split //, "abcde";

        The output of which was something like:

        $ perl test.pl a: 0.876941476789401; b: 0.5 c: 0.0768385185833438; d: 0.5 b: 0.203632482365844; c: 0.5 c: 0.234952540459695; a: 0.5 a: 0.746254431212112; d: 0.5 b: 0.648884088019244; e: 0.5 ebcda

        After running this a few times and looking at the results, it hit me. This is going to be a rough explanation, but I'll give it a shot. After comparing the first four letters to one another it selects the "smallest" one (the one with the smallest rand() anyway) and compares it to the final letter: 'e' in this case. Because 'e' is the last to be compared (in the initial go, at least) and because the rand() number is regenerated at each comparison, it always has roughly a 50% chance of being the "smallest" since it has to only pass a single test to be the "smallest". The letters in front of it must not only pass their first test, but will always have to be compared to the letters following it, thus reducing their chances of being the "smallest".

        In this way, my "solution" favors the letters towards the end and is more likely to stick them in front. That's why the distribution is so uneven. If you follow sort()'s algorithm ( quicksort, I believe (C's qsort(), I think ) ) through, it'll make sense.

        I hope that makes some sense ...

        Zenon Zabinski | zdog | zdog@perlmonk.org

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://389006]
Approved by Corion
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-26 07:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found