我正在处理设置分区问题,需要一种方法来定义无序存储桶大小的所有组合。给定N个元素和M个组,找到组大小的每个组合,使得组大小的总和为N.注意:桶的大小不能为0.
例如,假设需要将6个项目放在3个桶中。我正在寻找的解决方案是:
([1,2,3],[1,1,4],[2,2,2])
为了平等地映射这些,我使用map函数如下:
@grouping = map { int( ($items + $_) / $groups ) } 0 .. $groups-1;
为了获得所有组合,我正在考虑某种递归函数,其中每个递归级别N在数组中找到元素N的可能值。每个级别可以插入的符合条件的值是> = previousLevel。这就是我的想法,但必须有更好的方法来做到这一点......
sub getList($$@){
my $itemCount = shift;
my $groupCount = shift;
my @currentArray = @_;
my $positionToFill= @currentArray;
if($positionToFill == 0){
my $minValue = 1;
}
else{
my $minValue = currentArray[$positionToFill-1];
}
my $currentSum = sum(@currentArray);
return undef if $currentSum + $minValue >= $items;
my @possibleCombinations = ();
for(my $i = $minValue; $i < $items - $currentSum; $i++){
$currentArray[$positionToFill] = $i;
if($positionToFill == $groupCount-1){
push(@possibleCombinations, \@currentArray)
}
else{
push(@possibleCombinations, getList($itemCount, $groupCount, @currentArray);
}
}
return @currentArray;
}
答案 0 :(得分:1)
要将N个项目分组到M个组中,最终需要一个递归函数,将N-1个(或更少)项目分组到M-1个组中。
sub partition {
# @results is a list of array references, the part of the partitions
# created in previous iterations
my ($N, $M, @results) = @_;
if ($M == 1) {
# only one group. All elements must go in this group.
return map [ sort {$a <=> $b} @$_, $N ], @results;
}
# otherwise, put from 1 to $N/$M items in the next group,
# and invoke this function recursively
my @new_results = ();
for (my $n = 1; $n <= $N/$M; $n++) {
push @new_results, partition($N-$n, $M-1,
map [ @$_, $n ] @results);
}
return @new_results;
}
并使用
之类的调用启动该过程@all_partitions = partition(6, 3, []); # [] = list with one ref to an empty array
这种方法会产生一些你必须过滤掉的副本,但总的来说它会非常有效。