Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Need help with number generator

by meonkeys (Chaplain)
on Aug 09, 2000 at 23:11 UTC ( #27154=perlquestion: print w/replies, xml ) Need Help??
meonkeys has asked for the wisdom of the Perl Monks concerning the following question:

My Brethren and Sistren(?),
I need help with a number generator. I want "1" returned 80% of the time, "2" 10% of the time, and "3" 10% of the time. By "of the time" I mean, say, 1 out of 10 times a program is run, "print $a" will return "2", etc. Is there a more elegant way to do this than the following?
#!/usr/bin/perl -w @a = qw( 1 1 1 1 1 1 1 1 2 3 ); print $a[ int(rand 9) ];

Replies are listed 'Best First'.
Re: Need help with number generator
by ferrency (Deacon) on Aug 09, 2000 at 23:40 UTC
    Your answer is good and short for the small case you need to solve now; but that method could get out of hand if you need to solve the more general problem of picking randomly from a weighted set of items. In that case, and answer similar to Gryn's would be more appropriate.

    There was another thread at Biased random number selection which has information on how to optimize the selection process for speed.

    But here's the most straightforward (but not necessarily fastest... and not tested) solution:

    # A list of weights; initialized for your problem my @weights = (8, 1, 1); # Count the total of all the counts my $sum; map { $sum += $_; } @weights; # Choose a random number, then find a weighted random result my $target = rand($sum); for (1..@weights) { $target -= $weights[$_]; last if $target <= 0; } print "The number is $_\n";
    Hope this helps. Alan
Roulette wheel
by gryng (Hermit) on Aug 09, 2000 at 23:21 UTC
    (warning all code untested)

    If you aren't worried about space, then that approach is fine enough, but perhaps:

    @a = split /\s+/, join " ", ("1 " x 8, "2 " x 1, "3 " x 1); print $a[int(rand 10)];
    Is more(?) clear?

    However, I would not approach it this way. Instead, I would do something like:

    my %wheel = {"One" => 8, "Two" => 1, "Three" => 1}; my $sum; my $item; foreach $item (keys %wheel) { $sum+=$wheel{$item}; } my $spin = int(rand($sum)); foreach $item (keys %wheel) { last if $sum < $wheel{$item}; $sum-=$wheel{$item}; } print "$item was picked\n";
    I think that will work, if not, you should at least get the general idea.


Re: Need help with number generator
by Boogman (Scribe) on Aug 09, 2000 at 23:49 UTC
    how about $random = 2 + ( 8 <=> int( rand( 10 ) ) );
    The <=> returns -1, 0, or 1, so you'll get -1 + 2 = 1 if its less than 8, 2 if its equal to 8, or 3 if it is greater than 8.
      This idea rocks. But I think this is correct
      $random = 2 + ( int( rand( 10 ) ) <=> 8 );

      this will make
      $random = 1 ...... 80% of the time
      $random = 2 ...... 10% of the time
      $random = 3 ...... 10% of the time
Re: Need help with number generator
by turnstep (Parson) on Aug 10, 2000 at 00:15 UTC


    ($x) = map { $_<1 ? 2 : $_<2 ? 3 : 1} rand 9;
Re: Need help with number generator
by BlueLines (Hermit) on Aug 09, 2000 at 23:43 UTC
    here's another approach that assumes rand is actually random, which it technically isn't (see Math::TrulyRandom):
    my $seed=rand; my $result=($seed>=0) + ($seed>=.8) + ($seed>=.9); print "$result\n";


    Disclaimer: This post may contain inaccurate information, be habit forming, cause atomic warfare between peaceful countries, speed up male pattern baldness, interfere with your cable reception, exile you from certain third world countries, ruin your marriage, and generally spoil your day. No batteries included, no strings attached, your mileage may vary.
Re: Need help with number generator
by jettero (Monsignor) on Aug 09, 2000 at 23:44 UTC
    use strict; my %odds = ( 1 => 80, 2 => 10, 3 => 10 ); for my $i (1..25) { print "$i: ", &go, "\n"; } sub go { my $i = int(rand(100) + 1); foreach my $k (keys %odds) { $i -= $odds{$k}; return $k if $i <= 0; } return undef; }
Re: Need help with number generator
by lolindrath (Scribe) on Aug 09, 2000 at 23:41 UTC
    This maybe?
    $randnum = int(rand(100)); if ( $randnum > 20 ) #80% { #stuff for 1 } if ( $randnum > 90 ) #10% { #stuff for 2 and 3 }

      I don't want to be picky, but that would do #stuff for 1 70% of the time.

      It would do #stuff for 1 _and_ #stuff for 2 and 3 10% of the time.

      And 20% of the time it would do nothing...

      Remember to think before you code! :-P


RE: Need help with number generator
by sinan (Sexton) on Aug 10, 2000 at 15:36 UTC
    I also need something like that, but what I want to do is a little bit more complicated. I need to generate random numbers using the standard distribution, that is, I want the numbers in the middle to have a higher frequency than the others.

    Does anybody know an algorithm that does this?

RE: Need help with number generator
by Adam (Vicar) on Aug 09, 2000 at 23:41 UTC
    This sounds suspiciously like a home work assignment.

    Update: My apologies. I remember having that exact question in a first year cs class. I don't want to encourage CS students to post their home work and submit our answers.

      I'm done with school, for now.

      The question is for redirection of traffic at my company's website. There are two homepage test pages, and my boss wants 10% of our traffic directed to each.

      Even if it was homework, isn't it a cool problem?

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://27154]
Approved by root
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (3)
As of 2018-01-23 06:45 GMT
Find Nodes?
    Voting Booth?
    How did you see in the new year?

    Results (240 votes). Check out past polls.