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

This is a holdover from Friday - it was a slow day, and this is how my idle time was spent. The code below is supposed to generate a random number from a range of numbers provided and then pick another random number and stick that in a different variable. For each guess that it makes its supposed to return too high or too low and then make a new range between low and high. It dies this sort of - but it doesnt always seem to stay within the new range for new guesses. This is probably the 5th or 6th revision of the script - Ive tried a few different variations of the code.
use strict; my $low = 1; my $high = 10; my @range=($low .. $high); my $rand=int(rand(@range)); my $seed=-1; my $count=1; my $lastguess; while ( $seed != $rand ) { undef @range if ( $ count > 1); @range=($low .. $high); $lastguess = $seed; $seed = int(rand(@range)); if ( $seed < $rand ) { print "You're too low - $seed (last guess = $lastguess)\n"; $low=$seed+1; $count++; } elsif ( $seed > $rand ) { print "you're too high - $seed (last guess = $lastguess)\n"; $high=$seed-1; $count++; } } print "you got it in $count tries\n"; print "seed = $seed rand = $rand ( last guess = $lastguess )\n";
Im just not seeing what is wrong. any help is appreciated

Ted
--
"Men have become the tools of their tools."
  --Henry David Thoreau

Replies are listed 'Best First'.
Re: generating random numbers within a range
by moot (Chaplain) on May 09, 2005 at 19:34 UTC
    rand takes a scalar value and generates a randomish number in the range 0..value-1. You are passing it an array. Try this:
    # other code here $rand = int(rand($high - $low) + $low); # other code here
    Note that this will result in a number >= $low but < high.

      Let me suggest looking at Math::Random::OO -- particularly Math::Random::OO::UniformInt. (Disclaimer -- I wrote it). It will help avoid typical errors that creep in. (OBO, etc.)

      use Math::Random::OO 'UniformInt'; my $prng = UniformInt(1,10); print $prng->next; # integer between 1 and 10 inclusive

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: generating random numbers within a range
by ikegami (Patriarch) on May 09, 2005 at 19:34 UTC

    $seed and $rand are indexes into @range, but @range changes so you need to get the value at that index.

    use strict; use warnings; my $low = 1; my $high = 10; my @range=($low .. $high); my $rand=$range[rand(@range)]; # <--- my $seed=$low-1; # <--- Works for all values of $low + and $high. my $count=1; my $lastguess; while ( $seed != $rand ) { #undef @range if ( $ count > 1); # <--- Useless. @range=($low .. $high); $lastguess = $seed; $seed = $range[rand(@range)]; # <--- if ( $seed < $rand ) { print "You're too low - $seed (last guess = $lastguess)\n"; $low=$seed+1; $count++; } elsif ( $seed > $rand ) { print "you're too high - $seed (last guess = $lastguess)\n"; $high=$seed-1; $count++; } } print "you got it in $count tries\n"; print "seed = $seed rand = $rand ( last guess = $lastguess )\n";

    Added: As a bonus, here's cleaner code and output:

    use strict; use warnings; my $low = 1; my $high = 10; my $range = $high - $low + 1; my $rand = $low + int(rand($range)); my $count = 0; for (;;) { my $guess = $low + int(rand($range)); $count++; print("$count) Guessing $guess."); last if $guess == $rand; if ( $guess < $rand ) { print " Too low.\n"; $low = $guess + 1; } elsif ( $guess > $rand ) { print " Too high.\n"; $high = $guess - 1; } $range = $high - $low + 1; } if ($count == 1) { print " Got it on the first try!\n"; } else { print " Got it in $count tries.\n"; }

    It's "cleaner" because (1) a list (@range) isn't built where one isn't needed, (2) $count is updated when a guess is actually made, (3) $seed is more appropriately called $guess, and (4) $seed isn't checked against $guess until a guess is actually made. Also, (5) I removed the previous guess from the output, since (A) it would need to be omitted on the first guess, and (B) the previous guess is already displayed on the previous line.

      Thanks! Its a time waster, but it was driving me nuts...
      Ted
      --
      "Men have become the tools of their tools."
        --Henry David Thoreau

        By the way, if you want to guess the number in a guaranteed maximum number of guesses, divide the search space into two instead of randomly guessing.
        Change
        my $guess = $low + int(rand($range));
        to
        my $guess = $low + int($range/2);
        to see this in action. You'll find the number of guesses is never more than ceil(log2(range)) guesses. This is called a binary search.

        ceil(log2(x)) ------------- 1.. 8: 3 1.. 16: 4 1.. 32: 5 1.. 64: 6 1.. 128: 7 1.. 256: 8 1.. 512: 9 1.. 1024: 10 1.. 10: 4 1.. 100: 7 1.. 1,000: 10 1.. 10,000: 14 1.. 100,000: 17 1.. 1,000,000: 20 1..10,000,000: 24
Re: generating random numbers within a range
by cees (Curate) on May 09, 2005 at 19:47 UTC

    For a simple (and secure) way of generating random numbers bounded by a range, you can use the Crypt::Random module to do this

    perl -MCrypt::Random -e 'print Crypt::Random::makerandom_itv(Lower => +10, Upper => 20), $/ for (1..10)'

    - Cees