【问题标题】:How can I use memoization to improve the run time of this algorithm?如何使用 memoization 来提高该算法的运行时间?
【发布时间】:2020-08-10 07:09:59
【问题描述】:

问题说明:

如果我们可以为每个整数分配一个正号或负号 一组整数,有多少种方法可以将有符号整数相加 等于目标值?我们必须使用集合中的每个整数。

例如 [1, 2, 3, 2], 目标 = 0

两种方式 [-1, 2, -3, 2] 和 [1, -2, 3, -2]

我的解决方案如下(java)

public static void main(String[] args) {
  int[] nums = {1, 2, 3, 2};
  int x = helper(0, 0, nums, 0);
  System.out.println(x);
}

private static int helper(int step, int sumSoFar, int[] nums, int target) {
  if (step == nums.length) {
    return sumSoFar == target ? 1 : 0;
  }

  return
    helper(step + 1, sumSoFar + nums[step], nums, target)
        +
        helper(step + 1, sumSoFar - nums[step], nums, target);
}

我知道在蛮力解决方案中有许多可能的重复计算,但我不明白传入 sumSoFar 变量是否有效地形成了一种记忆技术?

如果没有,我该如何使用 memoization 来提高该算法的运行时性能?

【问题讨论】:

  • 空集也是答案吗?我看到您的递归中有多个值发生变化,我不确定记忆是否有帮助。
  • 我添加了我们必须使用集合中的每个整数的条件,因此空集不是有效的解决方案。
  • 对不起。我可以说通过sumSoFar 似乎不是任何形式的记忆。通常,我希望有一个额外的数组或映射来存储整个记忆值表(以记住以前的结果)。但正如我已经提到的,我没有看到利用记忆化来解决这个特定问题的方法。这并不意味着它不存在。只是不知道。此外,空集(如果有效)仅适用于0(我认为)。

标签: java algorithm recursion memoization


【解决方案1】:

您可能希望使用以下算法实现“记忆化”。我在递归中又添加了一个参数——rest,它可以是未见元素的绝对正数或绝对负数之和。因此,如果没有机会达到目标,它会中断递归。

使用这种方法,最坏的情况仍然是 O(2^n) - 即 [0,0,0,0],但实际上它更快。

注意:假设 nums 中的元素是正数,如果不是,您可以使它们在 O(n) 中。

public static void main(String[] args) {

    int[] nums = {1, 2, 3, 2};
    int totalSumSum = arraySum(nums);

    int x = helper(-1, 0, nums, 0, totalSumSum);
    System.out.println(x);

}


private static int helper(int step, int sumSoFar, int[] nums, int target, int rest) {

    if (step == nums.length-1) {
        return sumSoFar == target ? 1 : 0;
    }

    int nextStep = step+1;
    int nextSumPos = sumSoFar + nums[nextStep];
    int nextSumNeg = sumSoFar - nums[nextStep];
    int nextRest = rest - nums[nextStep];

    boolean pos = false;
    if ((nextSumPos > target && nextSumPos - nextRest > target) ||  
        (nextSumPos < target && nextSumPos + nextRest < target)) { /* do nothing */  }
    else { pos = true; }

    boolean neg = false;
    if ((nextSumNeg > target && nextSumNeg - nextRest > target) ||
        (nextSumNeg < target && nextSumNeg + nextRest < target)) { /* do nothing */ }
    else { neg = true; }

    if (pos && neg) {
        return helper(nextStep, nextSumPos, nums, target, nextRest)
            +  helper(nextStep, nextSumNeg, nums, target, nextRest);
    }else if (pos && !neg) {
        return helper(nextStep, nextSumPos, nums, target, nextRest);
    } else if (!pos && neg) {
        return helper(nextStep, nextSumNeg, nums, target, nextRest);
    } else { /* !pos && !neg */
        return 0;
    }

}

private static int arraySum(int[] nums) {
    int sum = 0;
    for (int i = 0; i < nums.length; i++) {
        sum += nums[i];
    }
    return sum;
}

【讨论】:

    【解决方案2】:

    你可以使用 hash map 来解决这个记忆问题(例如:Guava Table)

    Table<Integer, Integer, Integer> calculated = HashBasedTable.create();
    
    private static int helper(int step, int sumSoFar, int[] nums, int target) {
       if (step == nums.length) {
          return sumSoFar == target ? 1 : 0;
       }
    
       if (calculated.contains(step, sumSoFar)) {
           return calculated.get(step, sumSoFar)
       }
       int result = helper(step + 1, sumSoFar + nums[step], nums, target)
            +
            helper(step + 1, sumSoFar - nums[step], nums, target);
       calculated.put(step, sumSoFar, result);
       return result;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-26
      • 2012-05-08
      • 2019-04-18
      • 1970-01-01
      • 2017-01-27
      相关资源
      最近更新 更多