Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-cloud-platform/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Perl 找到求和到某个所需数的数的组合_Perl_Math - Fatal编程技术网

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”函数,它接收已经包含的数字列表和尚未包含的数字列表

  • 此函数将遍历所有尚未包含的数字
  • 如果在中添加该数字符合目标,则记录该组数字并继续
  • 如果小于目标值,则递归调用函数,并使用您正在查看的编号修改包含/排除列表
  • 否则,只需进入循环的下一步(因为如果您在那里,则没有必要尝试添加更多数字,除非您允许负数)
  • 最初调用此函数时,包含的列表为空,而未包含的列表包含完整的数字列表
您可以通过这种方式进行优化,例如传递总和,而不是每次重新计算。此外,如果您最初对列表进行排序,您可以根据以下事实进行优化:如果在列表中添加数字k会使您超过目标,那么添加k+1也会使您超过目标

希望这会给你一个足够好的开始。不幸的是,我的perl已经生锈了


虽然这是一个蛮力算法,有一些捷径,但它永远不会那么有效。

这是一个“为我做家庭作业”的问题吗

要确定地做到这一点,需要一个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