my $ColumnCount = 4; # or whatever we need it to be my $CategoryCount = 6; # or whatever we need it to be my %Weight = (); # TODO: initialize %Weight hash with values from 0 to Q-1 my %WeightCache = (); # to be filled with weights # TODO: initialize weight cache to handle references to # hashes with indexes 1..Q-1 # brute-force would iterate across combinations of 1...Q-1; # for 6 categories and 4 columns, this is 5 choose 3 i.e. 10 sub WeightSpan { my ($LowBound,$HighBound) = @_; my $WeightSum = 0; $WeightSum += $Weight{$LowBound++} while $LowBound<$HighBound; return $WeightSum; } sub IncrementCombination { my ($LowBound,$HighBound,@Combination) = @_; my $Idx = 0; while($Combination[$Idx-1]=$HighBound-$Idx) { return () if 1==$Idx+scalar @Combination; $Idx--; }; $Combination[$Idx-1]++; $Combination[++$Idx -1]++ while $Idx; return @Combination; } sub MaximumHeight { my ($LowBound,$HighBound,@Combination) = @_; my @IndexSpan = (); # TODO: auto-init @IndexSpan: 0...scalar @Combination-1 return max(map {$WeightCache{$Combination[$_]}{$Combination[$_+1]} ||= &WeightSpan($Combination[$_],$Combination[$_+1])} @IndexSpan); } sub CrunchBestCombination { my ($LowBound,$HighBound,$N) = @_; my @Combination = (); # TODO: auto-init: fill @Combination with 1...$N-1 my @BestCombination = @LowestBounds; my $BestHeight = &MaximumHeight($LowBound, $HighBound, @Combination); &IncrementCombination($LowBound, $HighBound, @Combination); while(@Combination) { my $CurrentMaxHeight = &MaximumHeight($LowBound,$HighBound,@Combination); if ($CurrentMaxHeight<$BestHeight) { $BestHeight = $CurrentMaxHeight; @BestCombination = @Combination; } @Combination = &IncrementCombination($LowBound, $HighBound, @Combination); } return ($BestHeight,@BestCombination); } my @TargetCombination = &CrunchBestCombination(1, $ColumnCount-1, $CategoryCount); my $BestHeight = shift(@TargetCombination); # TODO: translate @TargetCombination into # cell breaks