Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

random pairs

by Anonymous Monk
on Jul 13, 2012 at 18:57 UTC ( [id://981703]=perlquestion: print w/replies, xml ) Need Help??

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

Replies are listed 'Best First'.
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.
Re: random pairs
by CountZero (Bishop) on Jul 13, 2012 at 20:25 UTC
    Easy:
    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

      Let's say I used your bit of code and edited it a bit to match my needs. Currently I have this:

      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

        Perl "warnings" is trying to tell you that $element{"interactions"} is uninitialized (undef). That probably means you "forgot" to give it a value previously. Check the program- and data-flow and see where you went wrong.

        Probably @{$pop{$element{interactions}}} is not doing what you expect. I think perhaps you mean something more like @{$pop{$element}{'interactions'}}

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

    Cheers,

    JohnGG

Re: random pairs
by aitap (Curate) on Jul 13, 2012 at 19:26 UTC

    Try searching CPAN for combinatorics-related modules, for example, Math::Combinatorics or Algorithm::Combinatorics. Also, it may be easier to help you if you tell us the purbose of generating such pairs of numbers (maybe it can be optimised too).

    EDIT: example code

    #!/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.
Re: random pairs
by monsoon (Pilgrim) on Jul 13, 2012 at 21:45 UTC
    Without retries
    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.
      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
Re: random pairs
by Kenosis (Priest) on Jul 13, 2012 at 20:42 UTC

    Perhaps the following will do what you need:

    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!

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;
      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
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?

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.

      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

      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);

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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://981703]
Approved by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2024-03-29 15:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found