繁体   English   中英

艰难的递归任务

[英]Tough recursive task

作为测试准备的一部分,我一直在努力解决问题,我想我可以使用你的帮助。 我需要编写一个布尔方法,它接受带有整数的数组(正数和负数),如果数组可以拆分为两个等于组,则返回true,即每个组的数量等于另一个数组。 例如,对于这个数组:

int[]arr = {-3, 5, 12, 14, -9, 13};

该方法将返回true,因为-3 + 5 + 14 = 12 + -9 + 13。

对于这个数组:

int[]arr = {-3, 5, -12, 14, -9, 13};

该方法将返回false,因为即使-3 + 5 + 14 + -12 = -9 + 13,等式的每一侧的数字量也不等于。

对于数组:

int[]arr = {-3, 5, -12, 14, -9};

该方法将返回false,因为数组长度不均匀。

该方法必须是递归的,允许重载,每个辅助方法也必须递归,我不需要担心复杂性。

我一直试图解决这个问题三个小时,我甚至都没有显示代码,因为我所做的所有事情都远非解决方案。

如果有人至少可以给我一些伪代码,那就太棒了。

非常感谢你!

描述的问题是分区问题的一个版本。 首先请注意,您的公式相当于决定是否存在输入的子集,其总和高达所有元素总和的一半(需要为整数,否则实例无法求解,但这很容易校验)。 基本上,在每个递归步骤中,要确定是否要将第一个数字选择到子集中,从而导致不同的递归调用。 如果n表示元素的数量,则必须有n/2 (需要再次积分)项目。

Sum表示输入的总和,并且令Target := Sum / 2 ,其在后续中被假定为积分。 如果我们让

f(arr,a,count) := true
                  if there is a subset of arr summing up to a with
                  exactly count elements
                  false
                  otherwise

我们获得以下递归

f(arr,a,count) = (arr[0] == a && count == 1)
                 ||
                 (a == 0 && count == 0)
                 if arr contains only one element
                 f(arr\arr[0], a, count)
                 ||
                 f(arr\arr[0], a - arr[0], count -1)
                 if arr contains more than one element

在哪里|| 表示逻辑消除, &&表示逻辑连接, \\表示元素的删除。 非单例数组的两种情况对应于将arr的第一个元素选择为所需子集或其相对补码。 请注意,在实际实施中, a不会真正从数组中删除; 一个起始索引(用作附加参数)将初始化为0并在每次递归调用中增加,最终到达数组的末尾。

最后, f(arr,Target,n/2)产生所需的值。

您要求使用伪代码,但有时将其编写为Java也同样简单明了。

该解决方案的一般思想是尝试将每个数字添加到等式的左侧或右侧。 它跟踪递归中每一步的每一侧的计数和总和。 评论中有更多解释:

class Balance {
  public static void main(String[] args) {
    System.out.println(balanced(-3, 5, 12, 14, -9, 13));   // true
    System.out.println(balanced(-3, 5, -12, 14, -9, 13));  // false
  }

  private static boolean balanced(int... nums) {
    // First check if there are an even number of nums.
    return nums.length % 2 == 0
        // Now start the recursion:
        && balanced(
            0, 0,  // Zero numbers on the left, summing to zero.
            0, 0,  // Zero numbers on the right, summing to zero.
            nums);
  }

  private static boolean balanced(
      int leftCount, int leftSum,
      int rightCount, int rightSum,
      int[] nums) {
    int idx = leftCount + rightCount;
    if (idx == nums.length) {
      // We have attributed all numbers to either side of the equation.
      // Now check if there are an equal number and equal sum on the two sides.
      return leftCount == rightCount && leftSum == rightSum;
    } else {
      // We still have numbers to allocate to one side or the other.
      return
          // What if I were to place nums[idx] on the left of the equation?
          balanced(
              leftCount + 1, leftSum + nums[idx],
              rightCount, rightSum,
              nums)
          // What if I were to place nums[idx] on the right of the equation?
          || balanced(
              leftCount, leftSum,
              rightCount + 1, rightSum + nums[idx],
              nums);
    }
  }
}

这只是第一个想法的解决方案。 它是O(2 ^ n),对于大n来说显然相当慢,但是对于你给出的问题的大小来说这很好。

您的策略应该是尝试所有可能的组合。 我将尝试记录如何实现这一目标。

注意我认为要求:让每个函数都使用递归有点困难,因为我会通过省略一些帮助函数来解决这个问题,这些函数使得代码更具可读性,所以在这种情况下我不会这样做。

通过递归,您总是希望向最终解决方案发展,并检测您何时完成。 所以我们在函数中需要两个部分:

  1. 递归步骤:为此我们将获取输入集的第一个元素,并尝试如果我们将它添加到第一个集合会发生什么,如果找不到解决方案,我们将尝试将它添加到第二集。
  2. 检测我们何时完成,即输入集为空时,在这种情况下,我们要么找到了解决方案,要么我们没有找到解决方案。

我们的第一步中的一个技巧是,在获取集合的第一个元素之后,如果我们尝试对余数进行分区,我们不希望这两个集合相等,因为我们已经将第一个元素分配给其中一个集合。

这导致了遵循此策略的解决方案:

public boolean isValidSet(MySet<int> inputSet, int sizeDifferenceSet1minus2)
{
    if (inputSet.isEmpty())
    {
         return sizeDifferenceSet1minus2== 0;
    }

    int first = inptuSet.takeFirst();
    return isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ first)
              || isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ -1 * first);
}

此代码需要一些您仍需要实现的帮助功能。

它的作用是首先测试我们是否已达到结束条件,如果是,则返回此分区是否成功。 如果我们仍然在集合中留下了元素,我们会尝试如果将它添加到第一个集合中会发生什么,然后将其添加到第二个集合时会发生什么。 请注意,我们实际上并没有跟踪集合,我们只是跟踪集合1减去2之间的大小差异,减少(但您可以传递两个集合)。

另请注意,要使此实现起作用,您需要复制输入集而不是修改它!

对于一些背景信息:这个问题被称为分区问题 ,它以NP完全着称(这意味着它可能无法有效地解决大量输入数据,但很容易验证分区确实是一个解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM