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

I have a sorting problem that I do not know how to solve.
The description is that there are a number of different fruits arranged in rows that are stacked above each other.
Each row will have a limited number of the fruits and there will only be one of each fruit in each row. There may also be one or more positions in the row that are blank.
The task is to re-arrange the positions of the fruits in each row so that columns contain as much of the same fruit as possible.
The positions of the blanks does not matter at all.
The possible 16 fruits are listed below
Apple
Hawthorn
Pear
Apricot
Peach
Nectarines
Plum
Cherry
Currant
Gooseberry
Grapefruit
Kiwi
Rhubarb
Pawpaw
Melon
Figs

The Perl lines below
Defined the fruits including blanks with a b
Set the number of rows and columns in the display – note the number of columns must be less than the number of fruit
Randomly loads the fruit in each row so that any fruit only appears once in each row
Has two subs which:
1. Prints out the display contents as a comma separated list – this can be used in an Excel spreadsheet to more clearly see the positions of the fruit
2. Prints out the current hash of fruits so that this can be used an standard data
I would greatly value any comments about how to do the sort that I need.
If this were amplified by code that tackled the issue that would be brilliant!
use strict; my (@fr, $fr_num, %fr_cur, %dis, $row_num, $col_num, $jr, $jc, $random +_fr, $stored); #======================================== # # sub print_out_display # # this prints out to the screen a comma seperated list of the fruits i +n each row # # argument 1 referecne to the hash holding the fruit positions # #========================================= sub print_out_display($) { my ($ref_dis) = @_; my ($jr, $jc); print "\nPositions of fruit\n\n"; print ","; foreach $jc (sort {$a <=> $b} keys %{$ref_dis->{0}}) { print "$jc,"; } print "\n"; foreach $jr (sort {$a <=> $b} keys %$ref_dis) { print "$jr,"; foreach $jc (sort {$a <=> $b} keys %{$ref_dis->{$jr}}) { print "$ref_dis->{$jr}{$jc},"; } print "\n"; } } #======================================== # # sub store_display # # this prints out to the screen a comma seperated list of the hash and + its values # so this could be used repeatedly as standard data # # argument 1 reference to the hash holding the fruit positions # #========================================= sub store_display($) { my ($ref_dis) = @_; my ($jr, $jc); print "\nHash giving the positions of fruit\n"; foreach $jr (sort {$a <=> $b} keys %$ref_dis) { foreach $jc (sort {$a <=> $b} keys %{$ref_dis->{$jr}}) { print "\$dis{$jr}{$jc} = '$ref_dis->{$jr}{$jc}';\n"; } } } # this is the list of fruit there can only be one of each fruit in any + one row # b is a blank - there can be more that one of these in a row $fr[0] = 'Apple'; $fr[1] = 'Hawthorn'; $fr[2] = 'Pear'; $fr[3] = 'Apricot'; $fr[4] = 'Peach'; $fr[5] = 'Nectarines'; $fr[6] = 'Plum'; $fr[7] = 'Cherry'; $fr[8] = 'Currant'; $fr[9] = 'Gooseberry'; $fr[10] = 'Grapefruit'; $fr[11] = 'Kiwi'; $fr[12] = 'Rhubarb'; $fr[13] = 'Pawpaw'; $fr[14] = 'Melon'; $fr[15] = 'Figs'; $fr[16] = 'b'; $fr_num = scalar(@fr); # set the number of columns - this must be less than the number of rea +l fruits + 1 for a blank # with the data above it must be less than 17 $col_num = 10; # set the number of rows $row_num = 8; # load the intial position of the fruit # do this row by row for($jr = 0; $jr < $row_num; $jr ++) { # for each column in the row randomly select a fruit # undef the hash that holds the fruit used in the current row - blnaks + are not stored in this hash as there can be more than one undef %fr_cur; for($jc = 0; $jc < $col_num; $jc ++) { $stored = 'no'; while($stored eq 'no') { $random_fr = $fr[rand @fr]; if($random_fr ne 'b') { if(! exists($fr_cur{$random_fr})) { $dis{$jr}{$jc} = $random_fr; $fr_cur{$random_fr} = 1; $stored = 'yes'; } } else { $dis{$jr}{$jc} = $random_fr; $stored = 'yes'; } } } } print_out_display(\%dis); store_display(\%dis);

Replies are listed 'Best First'.
Re: A 2D Sorting problem
by shmem (Chancellor) on Jul 14, 2010 at 12:34 UTC

    How would you do that by hand? You would start swapping fruits until the result looks right, but to do this effectively you'd make up a strategy, since your biggest constraint is the number of hands you have, and this strategy surely includes counting. In a program you can hold all the items at once, in a suitable data structure.

    Since you are ordering fruits it makes little to no sense to have a hash keyed upon (x,y) position - anyways, an array of arrays would suit that better. You need a hash keyed with fruits and remeber the row position there. The column position is irrelevant, since your goal is to change that.

    Here is some code for starters. Is that a homework assignment?

    #!/usr/bin/perl # grid of fruits. my @grid = ( [ qw( Grapefruit b Apple Currant Cherry Hawthorn Pawpaw Plum Rhuba +rb Nectarines ) ], [ qw( Currant Grapefruit Rhubarb Plum Cherry Pear Nectarines Kiwi +Hawthorn Pawpaw ) ], [ qw( Apple Cherry Currant Apricot Hawthorn Pear Plum Kiwi Peach b + ) ], [ qw( Gooseberry Kiwi Cherry Apple Pawpaw Peach Hawthorn Pear Melo +n Currant ) ], [ qw( Pear Hawthorn Gooseberry Apricot Plum Melon Cherry Nectarine +s Apple Grapefruit ) ], [ qw( b Figs Cherry Rhubarb Melon Apple Plum Peach Gooseberry b ) +], [ qw( Hawthorn Rhubarb Figs Plum Melon Pear Nectarines Apricot Che +rry Kiwi ) ], [ qw( b Plum Cherry Rhubarb Kiwi Nectarines Currant Pear Gooseberr +y Pawpaw ) ], ); # remeber each fruits row ocurrence in a hash keyed upon fruit name my %fruitpos; for my $row (0..$#grid) { for my $col (0..$#{$grid[$row]}) { push @{$fruitpos{$grid[$row][$col]}}, $row; } } my @fruits = sort { @{$fruitpos{$b}} <=> @{$fruitpos{$a}} } keys %frui +tpos; # let's see how many fruits of each type we have print "$_: ",scalar @{$fruitpos{$_}}, "\n" for @fruits; # move the blanks to the end of the array { my $c; for (0..$#fruits) { $fruits[$_] eq 'b' and $c = $_ and last; } push @fruits, splice @fruits,$c,1; } # ok, let's abstract away the fruits into letters to make the grid loo +k nice. my $c = 'A'; my @letters; for (@fruits) { my $ary = delete $fruitpos{$_}; # substitue 'b' with '_' my $key = $_ eq 'b' ? '_' : $c; $fruitpos{$key} = $ary; push @letters, $key; $c++; # string increment } # put them back into a new grid. # But let's see first what the letter columns would look like; my @out; for my $col (0..$#letters) { my $ary = $fruitpos{$letters[$col]}; $out[$_][$col] = $letters[$col] for @$ary; } # substitute undefined elements in that grid with blanks for my $ary (@out) { $ary->[$_] ||= ' ' for 0..$#$ary } # mark the boundary of the original grid with a vertical bar splice(@$_,@{$grid[0]},0,'|') for @out; # output grid print join(" ",@$_),"\n" for @out; __END__ Cherry: 8 Plum: 7 Hawthorn: 6 Pear: 6 Kiwi: 5 Currant: 5 Nectarines: 5 Apple: 5 Rhubarb: 5 b: 5 Gooseberry: 4 Pawpaw: 4 Melon: 4 Peach: 3 Grapefruit: 3 Apricot: 3 Figs: 2 A B C F G H I | K N _ A B C D E F G I | K N A B C D E F H | M O _ A C D E F H J | K L M A B C D G H J | L N O A B H I J | L M P _ A B C D E G I | L O P A B D E F G I J | K _

    Next step would be to evaluate the vertical gaps and get the fruit column which fits best, e.g. column M would blend nicely into column G, K would go into H and so on.

    Your turn.

Re: A 2D Sorting problem
by jwkrahn (Abbot) on Jul 14, 2010 at 12:24 UTC

    Your %dis Hash of Hashes uses only numeric keys so you should probably use an Array of Arrays instead and then you wouldn't have to sort the keys.