this is something I've been cracking my brain over but can't seem to find a decent solution for. I've done a number of searches but can't seem to come up with anything useful either. Here's the situation: I have an array of integer numbers. I need to add up a number of those integers to match a specific value. So, say for example that I have the following array: my @array = (6, 18, 12, 2, 49); I'd need some sort of method to add up x of the elements to make up number y. So for example if I were to need 2 of the integers to add up to 30, I'd need it to return that elements 1 and 2 match the description. Normally if I knew in advance that I'd only need 2 integers I'd use 2 nested foreach loops, but in this case both the x amount of integers that need to be added up as well as the final number y that needs to result from the search are variable, so I'm definitely lost here. Is there a module that could work this black magic for me? On a sidenote I also need to mention that I require all matches.
After checking each of the replies and browsing through the various pieces of information available on the whole knapsack problem, first allow me to express my earnest gratitude for all the great advice everyone has spent their time giving me. Keeping in mind the specifics of my particular situation I decided to take little bits and pieces from everywhere and came up with the following:
package Algorithm::Knapsack::Fixed; use strict; use warnings; use Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(calculate); sub calculate { my($target, $number, $arrayref) = @_; my @counters = (0..($number - 1)); my @array = @{$arrayref}; my @results; my $endpoint = $#array - $#counters; #sort the array in descending order but remember what the original o +rder was so we can reverse the sort # before we give back the results. If for example we find that items + 123 and 456 of the sorted array # match, we'd return $order[123] and $order[456] my @order = sort { $array[$b] <=> $array[$a] } @0 .. $#array; @array = @array[@order]; while(1) { my $sum = 0; $sum += $array[$counters[0]]; for(1..$#counters) { $sum += $array[$counters[$_]]; if(($sum > $target) && ($_ < $#counters)) { &_increment($_, \@counters, \@array); last; } } if($sum == $target) { #we've got a matching combination! push(@results, [@order[@counters]]); } if(($counters[0] == $endpoint) || (($array[$counters[0]] * ($#coun +ters + 1)) < $target)) { return @results; } if($sum < $target) { &_increment(($#counters - 1), \@counters, \@array); next; } &_increment($#counters, \@counters, \@array); } } sub _increment { my($counter, $countersref, $arrayref) = @_; $countersref->[$counter]++; while(($countersref->[$counter] > $#$arrayref - ($#$countersref - $c +ounter)) && ($counter > 0)) { $counter--; $countersref->[$counter]++; } if($counter < $#$countersref) { foreach my $counter2 (($counter + 1)..$#$countersref) { $countersref->[$counter2] = $countersref->[$counter] + $counte +r2 - $counter; } } } 1;
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |