artist has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks

Here is my problem,

  1. A List is a list of given numbers.
  2. A Pair is a set of 2 numbers extracted from the List.
  3. A Row is a set of 'P' such pairs such that each number in the row is unique.
  4. P is a fixed number : Let's take it as 2.
  5. Goal: is to maximize the the number of rows.
I am able to extract the pairs and need suggestion in improving that and in reaching the goal.

Here is my code.

use strict; use warnings; my $Z; my @array; foreach (1..36){ $Z->{1 + int rand(8)}++; } foreach (sort { $Z->{$a} <=> $Z->{$b}} keys %{$Z}){ my $element = $_; foreach (1..$Z->{$element}){ push @array, $element; } } #my @array = qw(2 8 8 8 1 1 1 1 5 5 5 5 7 7 7 7 4 4 4 4 4 4 3 3 3 3 3 +3 3 6 6 6 6 6 6 6); print join " " => @array,"\n"; sub get_pairs { print "====================\n"; my @array = @_; my $X; my $hash; my $A; my $counter; my @pairs; foreach (@array){ $X->{$_} += 1; } foreach my $x (sort { $a <=> $b } keys %{$X}){ foreach my $y (sort { $a <=> $b } keys %{$X}){ next if $X->{$x} == 0; next if $x eq $y; next if $X->{$y} == 0; next if defined $hash->{$x}{$y}; $counter++; print "$counter = $x $y\n"; push @pairs, [$x,$y]; $hash->{$x}{$y} = $hash->{$y}{$x} = 1; $X->{$x} -= 1; $X->{$y} -= 1; } } print "Counts $counter\n"; } get_pairs(@array); __END__ 1 = 1 2 2 = 1 3 3 = 1 4 4 = 1 5 5 = 3 4 6 = 3 5 7 = 3 6 8 = 3 7 9 = 3 8 10 = 4 5 11 = 4 6 12 = 4 7 13 = 4 8 14 = 5 6 15 = 6 7 16 = 6 8 Counts 16
Thanks
artist.

Replies are listed 'Best First'.
Re: Pairing the pairs
by BrowserUk (Patriarch) on Jun 12, 2003 at 22:57 UTC

    I think this does the same thing as your code, but is a bit simpler.

    #! perl -slw use strict; use warnings; my @array = sort map{ 1+ int rand(8) } 1..36; print "@array"; print '=' x 30; my %X; $X{$_}++ for @array; my @keys = sort { $a <=> $b } keys %X; my %hash; my @pairs; foreach my $x ( @keys ){ foreach my $y ( @keys ){ next if not ( $X{$x} and $X{$y} ) or $x eq $y or defined $hash{$x}{$y}; push @pairs, [$x,$y]; print scalar @pairs, " = $x $y"; $hash{$x}{$y} = $hash{$y}{$x} = 1; $X{$x} -= 1; $X{$y} -= 1; } } print 'Count: ', scalar @pairs;

    Why do you use references to anonymous hashes instead of a straight hash? All it does it complicate the syntax for no benefit.

    To extend your solution to more than two elements per row, you might be able to use tye's Algorithm::Loops.

    I say "might" because your description of the problem is somewhat confusing. You say "A Pair is a set of 2 numbers", "A Row is a set of 'P' such pairs", & "P is a fixed number : Let's take it as 2", which by my reading should mean that each Row (line) in your sample output would consist of P pairs, ie. 2 sets of 2 numbers, but your output only shows one pair?

    Overall, I found your description of the problem completely confusing.

    What I think you are trying to do is:

    Generate the maximum number of unique sets of N unique numbers from a given array of numbers, using each of the (possibly duplicated) numbers in the input array exactly as many times as it appears in the input array.

    Does that sum up your intent?


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Why do you use references to anonymous hashes instead of a straight hash? All it does it complicate the syntax for no benefit.
      References have become second nature and thus for me it dosn't create complications. As an added benefit it helps when passed in subs.
      but your output only shows one pair?
      Yes, my code has only extracted pairs. Not the rows as I have written
      "I am able to extract the pairs and need suggestion in improving that and in reaching the goal."

      artist

Re: Pairing the pairs
by antirice (Priest) on Jun 12, 2003 at 22:11 UTC

    You may want to consider changing your pairing sub. Observe: (readmore is for comparison)

    sub get_pairs { my %hash = (); $hash{$_}++ foreach @_; my @keys = sort { $hash{$b} <=> $hash{$a} } keys %hash; my $counter = 0; foreach my $x (0..$#keys - 1) { next unless $hash{$keys[$x]}; foreach my $y ($x+1..$#keys) { last unless $hash{$keys[$x]}; next unless $hash{$keys[$y]}; $counter++; print "$counter = ",($keys[$x] < $keys[$y] ? "$keys[$x] $keys[$y +]":"$keys[$y] $keys[$x]"),$/; $hash{$keys[$x]}--; $hash{$keys[$y]}--; } } print "$counter counts$/" print "$counter counts$/" print Dumper(\%hash); #to see how many matches are made } get_pairs(@array); __DATA__ 1 = 3 6 2 = 4 6 3 = 1 6 4 = 6 7 5 = 5 6 6 = 6 8 7 = 2 6 8 = 3 4 9 = 1 3 10 = 3 7 11 = 3 5 12 = 3 8 13 = 1 4 14 = 4 7 15 = 4 5 16 = 4 8 17 = 1 7 17 counts $VAR1 = { '6' => 0, '8' => 0, '4' => 0, '1' => 0, '3' => 1, '7' => 0, '2' => 0, '5' => 1 };

    So what gives? In the sub I provided, the matching attempts to get rid of the largest groups first since these groups are more likely to create the most pairs.

    Update: I decided to put a complete solution together along with a comparison of the two subs.

    The greatest example I've seen thus far of why going according to number of times each unique number shows up can be shown if you use the following value for @array:

    @array = qw(6 6 5 5 1 1 1 3 3 3 3 2 2 2 2 4 4 4 4 4 8 8 8 8 8 8 8 8 7 +7 7 7 7 7 7 7); __DATA__ 6 6 5 5 1 1 1 3 3 3 3 2 2 2 2 4 4 4 4 4 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 +7 ==================== 1 = 1 2 2 = 1 3 3 = 1 4 4 = 2 3 5 = 2 4 6 = 2 5 7 = 3 4 8 = 3 5 9 = 4 6 10 = 4 7 11 = 6 7 12 = 7 8 12 counts $VAR1 = { '6' => 0, '3' => 0, '7' => 5, '2' => 0, '8' => 7, '1' => 0, '4' => 0, '5' => 0 }; 1 = 7 8 2 = 4 7 3 = 3 7 4 = 2 7 5 = 1 7 6 = 6 7 7 = 5 7 8 = 4 8 9 = 3 8 10 = 2 8 11 = 1 8 12 = 6 8 13 = 5 8 14 = 3 4 15 = 2 4 16 = 1 4 17 = 2 3 17 counts $VAR1 = { '6' => 0, '3' => 0, '7' => 1, '2' => 0, '8' => 1, '1' => 0, '4' => 0, '5' => 0 }; get_pairs1 produces: 36 rows get_pairs2 produces: 67 rows

    Not too shabby.

    antirice    
    The first rule of Perl club is - use Perl
    The
    ith rule of Perl club is - follow rule i - 1 for i > 1

      Ok, artist has informed me that each pair may appear in only one row. I must've missed that, and as such I'm offering a different version of maximize_rows. I put this in a reply as the other post was obscenely long.

      As you can see, grouping like elements together and then trying to cut the larger groups down first offers the maximum number of pairs and rows.

      Update:I have discovered a condition within the pairing subs that messes up the ability for maximum number of rows to be generated. I admit that my sub is more vulnerable to it than artist's. It occurs when there exists a particular number that has enough to match all the other numbers in the listing and the other numbers occur only once. As an example:

      As you can see, there's a problem in them there code. I haven't decided how to tackle this yet. Perhaps someone could lend a hand? :)

      Final Update (I hope): I've created a condition to find when and where it happens and now seems to work fantastic and stuff. :-P

      The demons have been exercised! This code is clear.

      antirice    
      The first rule of Perl club is - use Perl
      The
      ith rule of Perl club is - follow rule i - 1 for i > 1

Re: Pairing the pairs
by shemp (Deacon) on Jun 12, 2003 at 21:58 UTC
    I want to make sure i understand what you're trying to do. So, by your example, the numbers in List dont need to be unique. By #3, the 2 numbers need to be unique in the pairs you want.
    There is nothing in your conditions that says the same pair cannot appear more than once, just not in the same row. But, your code prohibits that case, with the line
    next if defined $hash->{$x}{$y};
    The code in your post is only generating pairs, not rows.
    Also, each element in the List is only used in 1 pair.


    If my understanding is correct, your get_pairs() function only gets 1 of many, many possible pair sets. Also, since you started with 36 elements in the array, and only 16 pairs were returned, there are still 4 elements in the array that werent assigned to pairs.

    Ok, if thats correct, with you fixing P=2, there should be 4 numbers in each row (2 pairs of 2 numbers). In general, there are 2*P numbers in each row.
    It may be easier to just try to create rows of 2P=4 unique numbers. Then, there are
    ( C(4,2)*C(2,2) ) / 2
    legal pairings for each row, since all numbers in each row are unique. (this is, of course if you allow the same pair in 2 different rows. If not, there are fewer pairings available.

    BTW, C(X,Y) means the number of combinations of X things taken Y at a time.

    UpdateThe formula was wrong for the number of possible pairings that can be picked from a row of unique numbers. I forgot that (a,b)(c,d) and (c,d)(a,b) were both being counted, so i added the /2
      The code in your post is only generating pairs, not rows. Also, each element in the List is only used in 1 pair.
      That is correct. And also a 'pair' cannot appear in more than 1 Row.
      Also, since you started with 36 elements in the array, and only 16 pairs were returned, there are still 4 elements in the array that werent assigned to pairs.
      Well, I like to capture unique pairings. Since the elements doesn't follow a particular pattern we cannot decide the number of pairs for sure. Also 16 is my number and that may not be the maximum. Remaining pairs could be repeats of earlier pairs which I don't want.

      Your logic could very well define the upper bound of the number of pairs however.

      artist.

Re: Pairing the pairs
by fglock (Vicar) on Jun 12, 2003 at 22:03 UTC

    I'm trying to understand it. Do you mean this?

    8 1 5 7 8 1 3 6 1 5 7 4 7 4 3 6 4 3 6 2 4 3 6 8 4 3 6 1 4 3 6 5 3 6 5 7

    update: fixed a typo

      Very well on the track, except that a pair can appear in maximum 1 row. In your solution (4,3) appears multiple time.

      artist

Re: Pairing the pairs
by aquarium (Curate) on Jun 12, 2003 at 22:04 UTC
    ...all this row stuff..hmm...your rows "P" just end up being pairs of numbers...and your problem looks like a non-problem, i.e. i can only see one (reasonable) solution for any input values = your pairs of numbers picked out...and it doesn't matter which pairs you pick. Elucidate some more, if i'm not getting the correct picture.