random numbers

M

Mei

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

A. Sinan Unur

[ 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
help yourself and help others help you.

Sinan
 
F

Fabian Pilkowski

* Mei said:
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);

Your numbers are already sorted numerically. After calling sort() on
them, your list looks like (10,2,3,5,7,8). 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
more readable to you):

@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 said:
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
 
X

xhoster

Mei said:
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
 
T

Thomas Kratz

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^.-
 
M

Mei

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++;
 
F

Fabian Pilkowski

* Mei said:
--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
 
D

Don Salad

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
calculating pi: you may inadvertently generate copyright
infringements, child pr0n, death threats, etc.

Thanks,
Don
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top