Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
Assuming I wanted to create pairs of numbers, i.e. (0, 16), for which the first number is predetermined and the second is selected at random. I am going to continually draw random integers between 0 and 99 to fulfill the latter end of the pair 100 times, which means my last pair would be something like (99, 53). I can't seem to be able to prevent some issues. I want to make it impossible for the rand function to draw the number I am picking for, i.e. (1,1). Also, I am considering the pair (1, 23) logically equivalent to (23, 1), so when I am drawing for the pair (23, ??), I don't want the second digit to be 1, therefore leaving only unique pairs. Any help/advice appreciated
Re: random pairs
by ww (Archbishop) on Jul 13, 2012 at 19:16 UTC
|
I'm sure there's some elegant way to do this... but since I'm not that good at elegance...
#pseudo...
draw 99 random numbers for the first value
knock out dups # which is do-able at part of step one by
# stuffing the drawn numbers as keys to a hash
rinse, repeat as necessary
draw 99 more;
combine as values for your hash
test that they satisfy your spec # except for the "make it impossible
+for
# the rand function ...."
# which ain't possible by any direct
+method!
remove unacceptable pairs # perhaps the test for (1,23) & (23,1
+)
#should occur in prior step
rinse, repeat
succeed.
| [reply] [d/l] |
Re: random pairs
by CountZero (Bishop) on Jul 13, 2012 at 20:25 UTC
|
use Modern::Perl;
my %test;
my @predetermined;
my @results;
push @predetermined, int(rand(100)) for 1 .. 100;
for my $element (@predetermined) {
my $pair;
{
$pair = int(rand(100));
redo if $test{$element.'-'.$pair} or $test{$pair.'-'.$element}
+ or $pair == $element;
}
$test{$element.'-'.$pair} = $test{$pair.'-'.$element} = 1;
push @results, $element.'-'.$pair;
}
say for sort(@results);
Update: Added test for both elements being equal.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] [d/l] |
|
my %test;
my @predetermined;
my @results;
@predetermined = (0..$pop-1);
for my $element (@predetermined)
{
my $pair;
{
$pair = int(rand($pop));
redo if $test{$element.'-'.$pair} or $test{$pair.'-'.$elem
+ent} or $pair == $element;
}
$test{$element.','.$pair} = $test{$pair.','.$element} = 1;
push @ {$pop{$element{interactions}}}, $pair;
push @results, $element.'-'.$pair;
foreach (@results)
{
($element.','.$pair) = split;
$element{$_} = $element;
}
}
sort {$a <=> $b} @results;
say for (@results);
For the line of code below,
push @ {$pop{$element{interactions}}}, $pair;
the terminal keeps returning this.
Use of uninitialized value $element{"interactions"} in hash element at disease_outbreak_array.pl line 87, <> line 1.
I have this hash that contains hashes of hashes that follow this format:
my %pop = (
0 => {status => "0", interactions => []},
1 => {status => "0", interactions => []},
I am attempting to store all the values that one person decides to interact with inside the "interactions" list within my hash of hashes, but it keeps giving me the uninitialized error.
TIA | [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
|
|
Re: random pairs
by johngg (Canon) on Jul 13, 2012 at 23:46 UTC
|
Seed a %seen hash with identical number pairs as we don't want those; sort and join the keys for non-repeating keys to save having to keep 6:17 and 17:6. Use a do { ... } until condition to keep choosing random numbers until we find one that isn't already in the %seen hash.
knoppix@Microknoppix:~$ perl -Mstrict -Mwarnings -E '
> my %seen;
> $seen{ $_ . q{:} . $_ } ++ for 0 .. 19;
> my @pairs;
> push @pairs, do {
> my $random;
> do { $random = int rand 20 } until
> not $seen{ join q{:}, sort { $a <=> $b } $_, $random } ++;
> [ $_, $random ];
> } for 0 .. 19;
> say sprintf q{%2d - %2d}, @{ $_ } for @pairs;'
0 - 14
1 - 14
2 - 5
3 - 10
4 - 7
5 - 4
6 - 18
7 - 2
8 - 12
9 - 8
10 - 14
11 - 17
12 - 18
13 - 16
14 - 6
15 - 19
16 - 2
17 - 10
18 - 1
19 - 11
knoppix@Microknoppix:~$
I hope this is helpful.
| [reply] [d/l] [select] |
Re: random pairs
by aitap (Curate) on Jul 13, 2012 at 19:26 UTC
|
#!/usr/bin/perl
use warnings;
use strict;
use Math::Combinatorics;
my @n = (0..99);
my $cmb = Math::Combinatorics->new(count => 2, data => [@n]);
my %numbers;
$numbers{$_} = [] for @n;
while (my ($x,$y) = $cmb->next_combination) { # never returns $x == 99
+, so $numbers{99} will be always empty :(
push $numbers{$x},$y;
}
print "($_,",$numbers{$_}->[int(rand($#{$numbers{$_}}+1))],")\n" for @
+n;
Sorry if my advice was wrong.
| [reply] [d/l] |
Re: random pairs
by monsoon (Pilgrim) on Jul 13, 2012 at 21:45 UTC
|
use strict;
use warnings;
my %pairs;
my @left = (0,1,2,5,7,8,10); #put your own predetermined numbers
foreach my $val1 (@left){
my @right = ();
foreach my $val2 (0..99){
push(@right, $val2) if $val1 != $val2 && (!exists $pairs{$val2} ||
+ $pairs{$val2} != $val1);
}
$pairs{$val1} = $right[int(rand($#right+1))];
}
foreach(@left){
print $_, ',', $pairs{$_}, ' ';
}
Update: modified condition so that it works correctly when there's a zero in the predetermined numbers. Also clean under 'use warnings;' now. | [reply] [d/l] |
|
This will breakdown when your list of predetermined numbers contains duplicates.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] |
Re: random pairs
by Kenosis (Priest) on Jul 13, 2012 at 20:42 UTC
|
use Modern::Perl;
my ( %pairsHash, $randVal );
for ( 0 .. 9 ) {
do { $randVal = int( rand(100) ) }
while ( ( exists $pairsHash{$randVal} and $pairsHash{$randVal} =
+= $_ )
or $randVal == $_ );
$pairsHash{$_} = $randVal;
}
say "($_, $pairsHash{$_})" for sort { $a <=> $b } keys %pairsHash;
Sample output for 10 pairs:
(0, 82)
(1, 50)
(2, 6)
(3, 72)
(4, 67)
(5, 71)
(6, 40)
(7, 93)
(8, 2)
(9, 22)
The script creates unique sets of two different numbers, where the second number (0 - 99) can be repeated.
Hope this helps! | [reply] [d/l] [select] |
Re: random pairs
by jwkrahn (Abbot) on Jul 14, 2012 at 09:32 UTC
|
#!/usr/bin/perl
use warnings;
use strict;
use List::Util 'shuffle';
my @random;
LOOP: {
@random = shuffle 0 .. 99;
my %unique;
for ( 0 .. $#random ) {
redo LOOP if $_ == $random[ $_ ] || ++$unique{ $_ < $random[ $
+_ ] ? "$_,$random[$_]" : "$random[$_],$_" } > 1;
}
}
my @pairs = map [ $_, $random[ $_ ] ], 0 .. $#random;
| [reply] [d/l] |
|
You assume that the "predetermined list" is an unbroken sequence of integers from 0 to x. How will you do if it is just a set of integers with duplicates and gaps?
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] |
Re: random pairs
by Anonymous Monk on Jul 13, 2012 at 19:11 UTC
|
Any help/advice appreciated Try wanting something else ? Posting some code? Like the one where you can't seem to be able to prevent some issues?
| [reply] |
Re: random pairs
by BillKSmith (Monsignor) on Jul 13, 2012 at 20:10 UTC
|
The second numbers of each pair must be the numbers 0 through 99 in random order. Code this directly.
use List::Util qw( shuffle );
my $n = 0;
my @pairs = map { [$n++, $_] } shuffle( 0..99 );
Update: Sorry guys, I misread the spec. | [reply] [d/l] |
|
I understood that the second element of each pair was to be a random number between 0 and 99. I did not read in the specs that there should be no repeats. Also it is very well possible that your method yields a combination, the reverse of which is also in the set. Suppose your shuffle yields the list 99 to 0. In that case, every result's reverse is in the set.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] |
|
Unfortunately, that algorithm does not avoid unwanted pairs like [1,23] and [23,1].
Here is a naive try until it works approach:
use strict;
use warnings;
use List::Util qw(shuffle);
my (%keys, @rnds, @pairs);
do {
%keys = ();
@rnds = shuffle (0..99);
@pairs = map{ my $r = $rnds[$_];
$keys{"$_,$r"}++;
$keys{"$r,$_"}++;
[ $_, $r ]
} (0..99);
} while ( scalar keys %keys != 200 ); # no symmetry means 200 distinct
+ pairs
# @pairs = shuffle( @pairs ); # uncomment if wanted
printf( "%3d %3d\n", @{$pairs[$_]}) for (0..99);
| [reply] [d/l] [select] |
Re: random pairs
by sundialsvc4 (Abbot) on Jul 13, 2012 at 21:08 UTC
|
The easiest if not the only way to obtain “random but unique draws” is always going to be to do what we do in real life: use a deck of cards. Create a list of 0..99 and then List::Util::shuffle that list ... quite literally like shuffling a deck of cards and then using it just as you do use a deck of cards in real life. You will shift the next number off the front of the list, and if you don’t like that one, simply push it onto the end and shift another. Exactly like putting the card on the bottom of the deck and drawing another top-card. (If it’s the same value, then the queue has degenerated to only one entry and you’re screwed ... you do need to test for that case and die. Although the odds of this happening are astronomically tiny, Murphy’s Law decrees that it will happen during your first and/or your most important demo.)
The odds are 99 to 1 that the number you draw will be acceptable, so you can simply loop through the list so-far to see if for any reason the number you have selected is disqualified.
another.
| [reply] |
|
shuffle is not the solution for this problem as it adds the unnecessary constraint that you should have no repeat from the set you shuffle.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] |
|
|