Perl 找到求和到某个所需数的数的组合
我需要一个算法来识别一组数字的所有可能组合,这些数字和其他一些数字相加 例如,给定集合Perl 找到求和到某个所需数的数的组合,perl,math,Perl,Math,我需要一个算法来识别一组数字的所有可能组合,这些数字和其他一些数字相加 例如,给定集合{2,3,4,7},我需要知道所有可能的子集总和x。如果x==12,答案是{2,3,7};如果x==7答案是{{3,4},{7}(即,两个可能的答案);如果x==8没有答案。请注意,正如这些示例所暗示的,集合中的数字不能重复使用 这是一个问题,但答案是用C#编写的,我需要用Perl编写,但我不知道如何翻译答案 我知道这个问题很难解决(请参阅其他帖子进行讨论),但我只需要一个蛮力解决方案,因为我处理的是相当小的集
{2,3,4,7}
,我需要知道所有可能的子集总和x
。如果x==12
,答案是{2,3,7}
;如果x==7
答案是{{3,4},{7}
(即,两个可能的答案);如果x==8
没有答案。请注意,正如这些示例所暗示的,集合中的数字不能重复使用
这是一个问题,但答案是用C#编写的,我需要用Perl编写,但我不知道如何翻译答案
我知道这个问题很难解决(请参阅其他帖子进行讨论),但我只需要一个蛮力解决方案,因为我处理的是相当小的集合。您可以使用模块生成元素列表的所有子集:粗略算法如下: 有一个“solve”函数,它接收已经包含的数字列表和尚未包含的数字列表
- 此函数将遍历所有尚未包含的数字李>
- 如果在中添加该数字符合目标,则记录该组数字并继续
- 如果小于目标值,则递归调用函数,并使用您正在查看的编号修改包含/排除列表
- 否则,只需进入循环的下一步(因为如果您在那里,则没有必要尝试添加更多数字,除非您允许负数)
- 最初调用此函数时,包含的列表为空,而未包含的列表包含完整的数字列表
虽然这是一个蛮力算法,有一些捷径,但它永远不会那么有效。这是一个“为我做家庭作业”的问题吗 要确定地做到这一点,需要一个N阶算法!(即,(N-0)*(N-1)*(N-2)…)对于大的输入集,速度将非常慢。但算法非常简单:计算出集合中每个可能的输入序列,然后尝试将序列中的输入相加。如果在任何一点上,总和匹配,则得到其中一个答案,保存结果并继续下一个序列。如果在任何一点上总和大于目标值,则放弃当前序列并继续下一个序列 您可以通过删除任何大于目标的输入来对此进行优化。另一种优化方法是获取序列中的第一个输入I并创建一个新序列S1,从目标T中减去I以获得一个新的目标T1,然后检查S1中是否存在T,如果存在,则得到一个匹配项,否则对S1和T1重复该过程。订单仍然是N!不过 如果你需要用一组非常大的数字来做这件事,那么我建议你阅读遗传算法
C.不久前,有人发布了一个类似的问题,另一个人展示了一个巧妙的外壳技巧来回答这个问题。这是一个shell技术,但我认为它不像我以前看到的那样是一个整洁的解决方案(因此我不认为这种方法值得称赞)。它很可爱,因为它利用了外壳扩展:
for i in 0{,+2}{,+3}{,+4}{,+7}; do
y=$(( $i )); # evaluate expression
if [ $y -eq 7 ]; then
echo $i = $y;
fi;
done
产出:
0+7 = 7
0+3+4 = 7
使用。这样,你可以提前决定要考虑什么大小的子集,并将内存使用最少。应用一些启发式方法提前返回
#!/usr/bin/perl
use strict; use warnings;
use List::Util qw( sum );
use Algorithm::Combinatorics qw( combinations );
my @x = (1 .. 10);
my $target_sum = 12;
{
use integer;
for my $n ( 1 .. @x ) {
my $iter = combinations(\@x, $n);
while ( my $set = $iter->next ) {
print "@$set\n" if $target_sum == sum @$set;
}
}
}
这些数字确实爆炸得相当快:需要花费数千天的时间才能通过一个40个元素集的所有子集。因此,您应该决定子集的有趣大小。sub Solve
sub Solve
{
my ($goal, $elements) = @_;
# For extra speed, you can remove this next line
# if @$elements is guaranteed to be already sorted:
$elements = [ sort { $a <=> $b } @$elements ];
my (@results, $RecursiveSolve, $nextValue);
$RecursiveSolve = sub {
my ($currentGoal, $included, $index) = @_;
for ( ; $index < @$elements; ++$index) {
$nextValue = $elements->[$index];
# Since elements are sorted, there's no point in trying a
# non-final element unless it's less than goal/2:
if ($currentGoal > 2 * $nextValue) {
$RecursiveSolve->($currentGoal - $nextValue,
[ @$included, $nextValue ],
$index + 1);
} else {
push @results, [ @$included, $nextValue ]
if $currentGoal == $nextValue;
return if $nextValue >= $currentGoal;
}
} # end for
}; # end $RecursiveSolve
$RecursiveSolve->($goal, [], 0);
undef $RecursiveSolve; # Avoid memory leak from circular reference
return @results;
} # end Solve
my @results = Solve(7, [2,3,4,7]);
print "@$_\n" for @results;
{
我的($goal$elements)=@;
#为了提高速度,您可以删除下一行
#如果@$元素保证已排序:
$elements=[sort{$a$b}@$elements];
我的(@results,$RecursiveSolve,$nextValue);
$RecursiveSolve=sub{
我的($currentGoal,$include,$index)=@;
对于(;$index<@$elements;++$index){
$nextValue=$elements->[$index];
#由于元素已排序,因此尝试
#非最终要素,除非小于目标/2:
如果($currentGoal>2*$nextValue){
$RecursiveSolve->($currentGoal-$nextValue,
[@$include,$nextValue],
美元指数+1);
}否则{
推送@results,[@$include,$nextValue]
如果$currentGoal==$nextValue;
如果$nextValue>=$currentGoal,则返回;
}
}#结束
}##结束$RecursiveSolve
$RecursiveSolve->($goal,[],0);
undef$RecursiveSolve;#避免循环引用的内存泄漏
返回@结果;
}#结束求解
my@results=Solve(7[2,3,4,7]);
为@results打印“@$\n”;
这是一个相当直接的翻译,但我简化了一点(现在又简化了一点,还删除了一些不必要的变量分配,添加了一些基于正在排序的元素列表的优化,并重新安排了条件,使其稍微更有效)
我现在还添加了另一个重要的优化。当考虑是否尝试使用未完成总和的元素时,如果该元素大于或等于当前目标的一半,则没有意义。(我们添加的下一个数字将更大。)根据您正在尝试的设置,这可能会短路更多。(您也可以尝试添加下一个元素,而不是乘以2,但是您必须担心列表的末尾。)谢谢,这很有帮助,但这有点太过粗暴了。我的电视机很小,b