递归:理解(subset-sum)包含/排除模式

时间:2012-11-30 09:27:19

标签: c++ recursion subset-sum

我需要了解这种递归是如何工作的,我理解简单的递归示例,但更高级的示例很难。即使只有两行代码,我也会遇到return语句本身的问题。我只是画了一个关于它如何工作的空白,特别是和/或运算符。任何见解都是非常受欢迎的。

      bool subsetSumExists(Set<int> & set, int target) {
      if (set.isEmpty()) {
         return target == 0;
      } else {
         int element = set.first();
         Set<int> rest = set - element;
         return subsetSumExists(rest, target)
             || subsetSumExists(rest, target - element);
         } 
      }

6 个答案:

答案 0 :(得分:5)

递归代码通常与缩减的概念相结合。通常,减少是通过某种变换将未知问题减少到已知问题的手段。

让我们来看看你的代码。您需要查找是否可以从输入数据集的元素构造给定的目标总和。 如果数据集为空,除了将目标总和与0进行比较之外,无需做任何事情。

否则,让我们应用减少量。如果我们从集合中选择一个数字,实际上可能有两种可能性 - 所选择的数字参与你正在寻求的数量,或者它没有。这里没有其他可能性(涵盖所有可能性非常重要!)。实际上,只要您可以涵盖剩余数据的所有可能性,选择哪个数据元素并不重要。

第一种情况:该数字不参与总和。我们可以将问题减少到一个较小的问题,数据集没有被检查的元素和相同的目标总和。

第二种情况:数字参与总和。我们可以将问题减少到一个较小的问题,数据集没有被检查的元素,请求的总和减少了数字的值。

注意,此时您不知道是否有任何这些情况属实。你只需要继续减少它们,直到你得到琐碎的空案例,你可以确切地知道答案。

如果对于这两种情况中的任何一种情况都是如此,原始问题的答案都是正确的。这正是operator ||所做的 - 如果它的任何操作数(2个案例的结果)都为真,它将产生true。

答案 1 :(得分:3)

||是符合逻辑的OR。它从左到右进行评估并短路。

这意味着在表达式A || B中,首先评估A。如果是true,则整个表达式为true,并且不会进行进一步的评估。如果Afalse,则评估B,并且表达式的值为B

在您的示例中,A是“尝试在不使用集合中的第一个元素的情况下获得相同的总和”。 B是“使用集合中的第一个元素,它将总剩余减少为总和,并尝试将其与元素的其余部分一起使用。”

答案 2 :(得分:2)

让我们先看看算法..

  • 基本情况(即递归终止的情况)是集合为空时。

  • 否则程序会将第一个元素从集合中减去。

  • 现在它将调用subsetSumExists(rest, target)并检查其是否为真, 如果是,它将返回true,否则它将调用 subsetSumExists(rest, target - element)并返回任何内容 回报。

简单来说,只有当第一个subsetSumExists(rest, target - element)返回false时,才会调用subsetSumExists(rest, target)

现在让我们尝试使用{3,5}的小样本集和8的总和来运行此代码。我将从现在开始调用函数sSE

sSE({3,5}, 8) => "sSE({5}, 8) || sSE({5},(8-3))"

sSE({5}, 8)   => sSE({}, 8) || sSE({}, (8-5)) 

sSE({}, 8)    => false.. now will call sSE({}, (8-5))

sSE({}, 3)    => false.. now will call sSE({5}, (8-3))

sSE({5}, 5)   => sSE({}, 5} || sSE({}, (5-5))

sSE({}, 5)    => false.. now will call sSE({}, (5-5))

sSE({}, 0)    => true.. ends here and return true

答案 3 :(得分:1)

要了解递归,您需要低估递归。 要做到这一点,你需要反复思考。

在这种特殊情况下。

对于任何:subsetSum(set, target)

  1. 如果set为空且target为0,则subsetSum存在

  2. 否则,请删除set的第一个元素。检查subdetSum(set, target)是否存在或subdetSum(set, target - removed_element)是否存在(使用步骤0)

答案 4 :(得分:1)

set减法看起来很奇怪,但我认为它意味着元素上的pop()。

虽然它是指数级的,但通过找到所有可能的组合“起作用”。

||语句中,LHS是包含当前元素的总和,RHS是除去它的总和。因此,您可以在指数树下,打开或关闭每个元素的每个组合。

顺便说一下,指数意味着如果你有30个元素,它将产生2到30的幂,即0x40000000或接近10亿个组合。

当然你可能会用完记忆。

如果找到解决方案,则可能无法通过所有2 ^ N个案例。如果没有解决方案,它将始终全部访问它们。

答案 5 :(得分:0)

如果我为自己说话,那么对问题的理解将源于||运算符。让我们用另一种方式看一下相同代码的底部return语句,

if (subsetSumExists(rest, target - element))
        return true;
if (subsetSumExists(rest, target))
        return true;

return false;