记忆递归调用时效率的巨大差异
Drastic efficiency difference when memoizing recursive calls
在执行 this LeetCode problem 时,我注意到性能有很大差异,具体取决于我决定编码的版本(请参阅注释部分)。其中一个更简洁,但我看不出应该有区别的原因。将不胜感激。
class Solution:
def rec_memo(self, nums, index, curr_sum, target, memo):
if curr_sum == target:
return True
elif curr_sum > target or index == len(nums):
return False
if (index, curr_sum) in memo:
return memo[(index, curr_sum)]
# This line (below) is significantly more efficient
# memo[(index, curr_sum)] = self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo) or self.rec_memo(nums, index + 1, curr_sum, target, memo)
# These lines (below) are significantly less efficient
# choose_num = self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo)
# not_choose_num = self.rec_memo(nums, index + 1, curr_sum, target, memo)
# memo[(index, curr_sum)] = choose_num or not_choose_num
return memo[(index, curr_sum)]
def canPartition(self, nums: List[int]) -> bool:
sum = 0
for i in range(0, len(nums), 1):
sum += nums[i]
if sum % 2 != 0:
return False
memo = {}
return self.rec_memo(nums, 0, 0, sum // 2, memo)
第一个:
self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo) or \
self.rec_memo(nums, index + 1, curr_sum, target, memo)
由于 short-circuit evaluation.,如果第一个 returns True
, 将不会评估对 self.rec_memo()
的后一个调用
然而,第二个:
choose_num = self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo)
not_choose_num = self.rec_memo(nums, index + 1, curr_sum, target, memo)
memo[(index, curr_sum)] = choose_num or not_choose_num
无论第一次调用 rec_memo()
的结果如何,您总是会进行第二次递归调用。您观察到的减速可能是这些额外递归调用的结果。
在执行 this LeetCode problem 时,我注意到性能有很大差异,具体取决于我决定编码的版本(请参阅注释部分)。其中一个更简洁,但我看不出应该有区别的原因。将不胜感激。
class Solution:
def rec_memo(self, nums, index, curr_sum, target, memo):
if curr_sum == target:
return True
elif curr_sum > target or index == len(nums):
return False
if (index, curr_sum) in memo:
return memo[(index, curr_sum)]
# This line (below) is significantly more efficient
# memo[(index, curr_sum)] = self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo) or self.rec_memo(nums, index + 1, curr_sum, target, memo)
# These lines (below) are significantly less efficient
# choose_num = self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo)
# not_choose_num = self.rec_memo(nums, index + 1, curr_sum, target, memo)
# memo[(index, curr_sum)] = choose_num or not_choose_num
return memo[(index, curr_sum)]
def canPartition(self, nums: List[int]) -> bool:
sum = 0
for i in range(0, len(nums), 1):
sum += nums[i]
if sum % 2 != 0:
return False
memo = {}
return self.rec_memo(nums, 0, 0, sum // 2, memo)
第一个:
self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo) or \
self.rec_memo(nums, index + 1, curr_sum, target, memo)
由于 short-circuit evaluation.,如果第一个 returns True
, 将不会评估对 self.rec_memo()
的后一个调用
然而,第二个:
choose_num = self.rec_memo(nums, index + 1, curr_sum + nums[index], target, memo)
not_choose_num = self.rec_memo(nums, index + 1, curr_sum, target, memo)
memo[(index, curr_sum)] = choose_num or not_choose_num
无论第一次调用 rec_memo()
的结果如何,您总是会进行第二次递归调用。您观察到的减速可能是这些额外递归调用的结果。