Velocity Reviews > Perl > random numbers

# random numbers

Mei
Guest
Posts: n/a

 06-06-2005
Hi,

I want to generate 6 random numbers from 1 to 10.

The 6 numbers must not the same.

These numbers must match to the number of an array.

I like to know how many iterations to get the match.

I am having troubles with the do-until loop. Could someone point out the
problem for me?

#!/usr/bin/perl -w

# generate 6 random numbers (1~10) that match exactly to the @ticket

# count the number of loops

use strict;

srand(time||\$\$);

my @ticket = sort (2, 3, 5, 7, 8, 10);

my @all_number = ();

my @new_number = ();

my \$count = 0;

# generate 10 numbers

for (my \$i = 0; \$i < 10; \$i++) {

my \$number = \$i + 1;

push (@all_number, \$number)

}

do {

#reset @new_number to the empty array each time through the loop.

@new_number = ();

#get 6 random numbers

for (my \$i = 0; \$i < 6; \$i++) {

my \$position = randomposition (@all_number);

# Pick a random number from @all_number

my \$number = splice (@all_number, \$position, 1);

push (@new_number, \$number);

}

@new_number = sort (@new_number);

print "@new_number\n";

\$count++;

} until ((\$new_number[0] eq "\$ticket[0]") && (\$new_number[1] eq
"\$ticket[1]")

&& (\$new_number[2] eq "\$ticket[2]") && (\$new_number[3] eq
"\$ticket[3]")

&& (\$new_number[4] eq "\$ticket[4]") && (\$new_number[5] eq
"\$ticket[5]"));

print \$count, "\n";

################################################## ########

sub randomposition {

my(@all_number) = @_;

# This expression returns a random number

return int(rand(scalar(@all_number)));

}

A. Sinan Unur
Guest
Posts: n/a

 06-06-2005
"Mei" <(E-Mail Removed)> wrote in news:d81eva\$h2i\$(E-Mail Removed):

[ what's up with the extra line-feeds? ]

> I want to generate 6 random numbers from 1 to 10.
> The 6 numbers must not the same.

> These numbers must match to the number of an array.
> I like to know how many iterations to get the match.

I am not sure what you mean by this.

> #!/usr/bin/perl
>
> use strict;

use warnings;

is better because it allows you to turn warnings for selected lexical
scopes.

> srand(time||\$\$);

Why? Note that, unless your Perl is severely broken, this is equivalent
to srand(time). From perldoc -f srand:

In versions of Perl prior to 5.004 the default seed was just the
current "time". This isn't a particularly good seed, so many old
programs supply their own seed value (often "time ^ \$\$" or "time
^ (\$\$ + (\$\$ << 15))"), but that isn't necessary any more.

> my @ticket = sort (2, 3, 5, 7, 8, 10);

> my @all_number = ();
> my @new_number = ();
> my \$count = 0;

Declare variables in the smallest applicable scope.

> # generate 10 numbers
>
> for (my \$i = 0; \$i < 10; \$i++) {
>
> my \$number = \$i + 1;
>
> push (@all_number, \$number)
>
> }

So, instead of this for loop, use:

my @all_number = (1 .. 10);

> do {
> #reset @new_number to the empty array each time through the loop.
> @new_number = ();

my @new_number();
>
> #get 6 random numbers
> for (my \$i = 0; \$i < 6; \$i++) {
> my \$position = randomposition (@all_number);
> # Pick a random number from @all_number
> my \$number = splice (@all_number, \$position, 1);
> push (@new_number, \$number);
> }

This is realy convoluted (and therefore confuses me still further as to
what you are trying to do).

for (1 .. 6) {
push @new_number, \$all_number[ int(rand( scalar @all_number )) ];
}

It seems to me like what you are really doing is to check how long it
would take for you to generate the sequence (0, 1, 2, 3, 4, 5) by
randomly selecting (with replacement) from the set {0, 1, 2, 3, 4, 5}.

Here is one way of doing it:

# !/usr/bin/perl

use strict;
use warnings;

my @ticket = sort {\$a <=> \$b } (2, 3, 5, 7, 8, 10);
my \$ticket = "@ticket";

my \$count = 0;

while ( 1 ) {
++\$count;

my @new;
for (1 .. 6) {
push @new, \$ticket[ int(rand( scalar @ticket )) ];
}

@new = sort {\$a <=> \$b } @new;
my \$new = "@new";

print "\$new\tvs\t\$ticket\n";

last if \$new eq \$ticket;
}

print "Count = \$count\n";

__END__

Please do read the posting guidelines for this group tolearn how you can

Sinan

--
A. Sinan Unur <(E-Mail Removed)>
(reverse each component and remove .invalid for email address)

comp.lang.perl.misc guidelines on the WWW:

Fabian Pilkowski
Guest
Posts: n/a

 06-06-2005
* Mei schrieb:
>
> I want to generate 6 random numbers from 1 to 10.
> The 6 numbers must not the same.
> These numbers must match to the number of an array.
> I like to know how many iterations to get the match.
>
> I am having troubles with the do-until loop. Could someone point out the
> problem for me?
>
> #!/usr/bin/perl -w
> # generate 6 random numbers (1~10) that match exactly to the @ticket
> # count the number of loops
> use strict;
> srand(time||\$\$);

You don't have to call srand() explicitly. It is called implicitly at
the first use of rand() since your Perl isn't very old. Have a look at
`perldoc -f srand` for details.

>
> my @ticket = sort (2, 3, 5, 7, 8, 10);

them, your list looks like (10,2,3,5,7,. This doesn't matter since you
sort your numbers in the same order later -- but be aware of this.

>
> my @all_number = ();
> my @new_number = ();
> my \$count = 0;
>
> # generate 10 numbers
> for (my \$i = 0; \$i < 10; \$i++) {
> my \$number = \$i + 1;
> push (@all_number, \$number)
> }

To get a list with all numbers from 1 to 10, please use

my @all_number = 1 .. 10;

>
> do {
> #reset @new_number to the empty array each time through the loop.
> @new_number = ();
>
> #get 6 random numbers
> for (my \$i = 0; \$i < 6; \$i++) {
> my \$position = randomposition (@all_number);
> # Pick a random number from @all_number
> my \$number = splice (@all_number, \$position, 1);
> push (@new_number, \$number);
> }

Consider to use map instead of this for loop. And IMHO is splice() too
much pverhead for this -- you could access each array element directly
since you know its position. Try to use one of (I don't know which is

@new_number = map { \$all_number[ rand @all_number ] } 0 .. 5;
@new_number = @all_number[ map { rand @all_number } 0 .. 5 ];

>
> @new_number = sort (@new_number);

This is what I've meant above. This sort() doesn't sort your numbers
numerically too. But it's just important to sort the numbers in the same
order as above (and this is what you're doing). If -- and only if -- you
left out the sort statement above, you should sort numerically here, I
think your code gets more clarity then.

@new_number = sort { \$a <=> \$b } @new_number;

>
> print "@new_number\n";
> \$count++;
> } until ((\$new_number[0] eq "\$ticket[0]") && (\$new_number[1] eq "\$ticket[1]")
> && (\$new_number[2] eq "\$ticket[2]") && (\$new_number[3] eq "\$ticket[3]")
> && (\$new_number[4] eq "\$ticket[4]") && (\$new_number[5] eq "\$ticket[5]"));

To compare numbers there is the == operator, eq is for comparing strings
only. I haven't checked it out yet, but AFAIK the numerical ones should
be a little bit faster. And well, it's not funny to read such repeated
code fragments. Perhaps you could use grep() instead.

} until ! grep { \$ticket[\$_] != \$new[\$_] } 0 .. 5;

>
> print \$count, "\n";
>
> ################################################## ########
>
> sub randomposition {
> my(@all_number) = @_;
> return int(rand(scalar(@all_number)));
> }

This sub could be shortened to

sub randomposition { int rand @_ }

and since writing "int rand @_" is shorter than "randomposition" you
could omit to declare a sub for such a triviality.

All in one, I would write your script as something like:

#!/usr/bin/perl -w
use strict;

my @ticket = sort 2, 3, 5, 7, 8, 10;
my @all = 1 .. 10;
my @new;
my \$count = 0;

do {
@new = sort @all[ map { rand @all } 0 .. 5 ];
\$count++;
} until ! grep { \$ticket[\$_] != \$new[\$_] } 0 .. 5;

print \$count;
__END__

regards,
fabian

xhoster@gmail.com
Guest
Posts: n/a

 06-06-2005
"Mei" <(E-Mail Removed)> wrote:
> Hi,
>
> I want to generate 6 random numbers from 1 to 10.
>
> The 6 numbers must not the same.

my %h;

\$h{int(rand(10)+1)} =1 while (6 > keys %h) ;

# the keys of %h are now 6 distinct random numbers between 1 and 10.

(I couldn't figure out how your code pertained to what you said you wanted
above, so I ignored it)

Xho

--
Usenet Newsgroup Service \$9.95/Month 30GB

Thomas Kratz
Guest
Posts: n/a

 06-06-2005
Fabian Pilkowski wrote:
> * Mei schrieb:
>
>>I want to generate 6 random numbers from 1 to 10.
>>The 6 numbers must not the same.
>>These numbers must match to the number of an array.
>>I like to know how many iterations to get the match.

> All in one, I would write your script as something like:
>
>
> #!/usr/bin/perl -w
> use strict;
>
> my @ticket = sort 2, 3, 5, 7, 8, 10;
> my @all = 1 .. 10;
> my @new;
> my \$count = 0;
>
> do {
> @new = sort @all[ map { rand @all } 0 .. 5 ];
> \$count++;
> } until ! grep { \$ticket[\$_] != \$new[\$_] } 0 .. 5;
>
> print \$count;
> __END__

or if only the number of iterations is of interest:

use strict;
use warnings;

my %ticket = map { \$_ => 1 } qw/2 3 5 7 8 10/;
my \$count = 0;
\$count++ && delete \$ticket{int(rand(10))+1} while keys %ticket;
print \$count;

Thomas

--
\$/=\$,,\$_=<DATA>,s,(.*),\$1,see;__END__
s,^(.*\043),,mg,@_=map{[split'']}split;{#>J~.>_an~>>e~......>r~
\$_=\$_[\$%][\$"];y,<~>^,-++-,?{\$/=--\$|?'"':#..u.t.^.o.P.r.>ha~.e..
'%',s,(.),\\$\$/\$1=1,,\$;=\$_}:/\w/?{y,_, ,,#..>s^~ht<._..._..c....
print}:y,.,,||last,,,,,,\$_=\$;;eval,redo}#.....>.e. r^.>l^..>k^.-

Mei
Guest
Posts: n/a

 06-06-2005
Hi,

--Thanks all. The reason that I did not use 1..10 is because the six numbers
must be different.

--Once I take off the do-until loop, you will see the result like: ¡§1 3 4 6
8 10 vs 2 3 5 7 8 10¡¨. So far, this works.

-- But as you pointed out, I want to see how long would it take for me to
generate the sequence (2, 3, 5, 7, 8, 10). So far, I am still having
troubles.

###### modified from the previous script #####

#!/usr/bin/perl -w

# generate 6 random numbers (1~10) that match exactly to the @ticket

# count the number of loops

use strict;

my @ticket = sort {\$a <=> \$b} (2, 3, 5, 7, 8, 10);

my \$ticket = "@ticket";

my @all_number = ();

my @new_number = ();

my \$count = 0;

# generate 10 numbers

for (my \$i = 0; \$i < 10; \$i++) {

my \$number = \$i + 1;

push (@all_number, \$number)

}

for (my \$i = 0; \$i < 6; \$i++) {

my \$position = int(rand(scalar(@all_number)));

# Pick a random number from @all_number

my \$number = splice (@all_number, \$position, 1);

push (@new_number, \$number);

}

@new_number = sort {\$a <=> \$b} (@new_number);

my \$new_number = "@new_number";

print "\$new_number\tvs\t\$ticket\n";

\$count++;

Fabian Pilkowski
Guest
Posts: n/a

 06-06-2005
* Mei schrieb:
>
> --Thanks all. The reason that I did not use 1..10 is because the six numbers
> must be different.

The "1..10" has almost nothing to do with your 6 numbers. "1..10" is
generating ten numbers, not six.

>
> --Once I take off the do-until loop, you will see the result like: ¡§1 3 4 6
> 8 10 vs 2 3 5 7 8 10¡¨. So far, this works.
>
> -- But as you pointed out, I want to see how long would it take for me to
> generate the sequence (2, 3, 5, 7, 8, 10). So far, I am still having
> troubles.

You don't mention which kind of troubles you have.

>
> ###### modified from the previous script #####
>
> #!/usr/bin/perl -w
> # generate 6 random numbers (1~10) that match exactly to the @ticket
> # count the number of loops
> use strict;
>
> my @ticket = sort {\$a <=> \$b} (2, 3, 5, 7, 8, 10);
> my \$ticket = "@ticket";
> my @all_number = ();
> my @new_number = ();
> my \$count = 0;
>
> # generate 10 numbers
> for (my \$i = 0; \$i < 10; \$i++) {
> my \$number = \$i + 1;
> push (@all_number, \$number)
> }

This for loop is generating the numbers from 1 to 10 and saves them in
the array @all_number -- not more, not less. This has absolutely nothing
to do with your six different numbers you want to get afterwards. And as
Sinan has pointed out you could replace this with

@all_number = 1 .. 10;

This is absolutely equivalent, but shorter -- and better to read.

>
> for (my \$i = 0; \$i < 6; \$i++) {
> my \$position = int(rand(scalar(@all_number)));
> # Pick a random number from @all_number
> my \$number = splice (@all_number, \$position, 1);
> push (@new_number, \$number);
> }

This for loop *calculates* your six random numbers. First, it gets a
random position in the array. With splice() you ask for the specified
element in your array to push it into the array @new_number. This could
be simplified to

for ( 1 .. 6 ) {
my \$pos = int rand @all_number; # no scalar() needed
my \$number = \$all_number[ \$pos ]; # no splice() needed
push @new_number, \$number;
}

You could write this all in one, without saving some values in \$pos and
\$number. That is, what Sinan has written:

for ( 1 .. 6 ) {
push @new_number, \$all_number[ rand @all_number ];
}

Sure, you could use map() instead of a for loop. That is, what I've done
in my posting:

@new_numbers = map { \$all_number[ rand @all_number ] } 1 .. 6;

Remember that this are just improvements to the code you've posted. I
think, your problem is to get six *different* numbers. Neither your code
nor our improvements will do this. But Xho has told you how this could
be done with a hash:

my %hash;
\$hash{ int rand @all_number } = 1 while keys %hash < 6;
@new_numbers = @all_number[ keys %hash ];

Another solution is to use List::Util's shuffle algorithm and cut off a
subarray of six elements. This could be more readable, especially if
you're not so familiar with hashes.

use List::Util qw( shuffle );
@new_numbers = ( shuffle @all_number )[ 0 .. 5 ];

Please, try to run the following script:

#!/usr/bin/perl -w
use strict;
use List::Util qw( shuffle );

my @ticket = sort 2, 3, 5, 7, 8, 10;
my @all = 1 .. 10;
my \$count = 0;

while ( ++\$count ) {
# get six different elements from @all
my @new = sort +( shuffle @all )[ 0 .. 5 ];

# print out for debugging
print "@ticket vs @new\n";

# stop if @ticket and @new are equal
last if "@ticket" eq "@new";
}

print \$count;
__END__

regards,
fabian

Guest
Posts: n/a

 06-07-2005
> I want to generate 6 random numbers from 1 to 10.
>
> The 6 numbers must not the same.
>
> These numbers must match to the number of an array.
>
> I like to know how many iterations to get the match.

That should be OK, but it's VERY IMPORTANT not to generate too many
random numbers, or you could end up with the same problem as
infringements, child pr0n, death threats, etc.

Thanks,
Don