【问题标题】:Ruby compute cumulative sum from endpoints recursively?Ruby递归地计算端点的累积和?
【发布时间】:2014-06-10 08:47:51
【问题描述】:

给定两个数字,比如说 (14, 18),问题是递归地找到这个范围内所有数字的总和,14,15,16,17,18。现在,我已经使用循环来完成此操作,但递归执行此操作时遇到了麻烦。

这是我的递归解决方案:

def sum_cumulative_recursive(a,b)

    total = 0

    #base case is a == b, the stopping condition
    if a - b == 0 
        puts "sum is: "
        return total + a 
    end

    if b - a == 0
        puts "sum is: "
        return total + b
    end

    #case 1: a > b, start from b, and increment recursively
    if a > b
        until b > a
            puts "case 1"
            total  = b + sum_cumulative_recursive(a, b+1)
            return total
        end
    end

    #case 2: a < b, start from a, and increment recursively
    if a < b
        until a > b
            puts "case 2"
            total = a + sum_cumulative_recursive(a+1, b)
            return total
        end 
    end
end

以下是一些示例测试用例:

puts first.sum_cumulative_recursive(4, 2)
puts first.sum_cumulative_recursive(14, 18)
puts first.sum_cumulative_recursive(-2,-2)

我的解决方案适用于 a > b 和 a

如何修复此代码以使其正常工作?

感谢您的宝贵时间。

【问题讨论】:

  • 听起来像是功课。它闻起来像家庭作业。是作业吗,宝贝?咕噜,咕噜。
  • a==b时应该是什么结果?
  • 不,这是一个实习职位的面试问题。
  • return a+b + result(a+1,b) 并包含一个情况,如果 a==b 返回 0,那么它将级联初始调用,您将得到一个结果
  • 递归?哇,我本来想说(14..18).reduce(:+)。 ;-)

标签: ruby recursion


【解决方案1】:
def sum_cumulative_recursive(a,b)
  return a if a == b
  a, b = [a,b].sort
  a + sum_cumulative_recursive(a + 1, b)
end

编辑

这是我从一些非正式基准中看到的最有效的解决方案:

def sum_cumulative_recursive(a,b)
  return a if a == b
  a, b = b, a if a > b
  a + sum_cumulative_recursive(a + 1, b)
end

使用:

Benchmark.measure { sum_cumulative_recursive(14,139) }

我最初响应的基准:0.005733

@Ajedi32 响应的基准:0.000371

我的新响应的基准:0.000115

我还惊讶地发现,在某些情况下,递归解决方案的效率接近或超过更自然的inject 解决方案的效率:

Benchmark.measure { 10.times { (1000..5000).inject(:+) } }
# =>   0.010000   0.000000   0.010000 (  0.027827)

Benchmark.measure { 10.times { sum_cumulative_recursive(1000,5000) } }
# =>   0.010000   0.010000   0.020000 (  0.019441)

如果你走得太远,你会遇到stack level too deep 错误......

【讨论】:

  • 我不明白永远不会使用的基准测试方法的意义。如果想要一个范围内元素的总和,他们肯定会直接从端点计算它。
  • 我并不是说基准测试确定了最佳解决方案。我主要只是好奇地想看看提问者的约束是如何发生的(例如,在我的初始响应中实例化一个数组的成本是多少)。当然,在正常情况下,我会按照上面@Ajedi32 的说明执行(14..18).inject(:+)
  • 感谢您分享您的见解,这是一个非常好的解决方案。
【解决方案2】:

我会这样做:

def sum_cumulative_recursive(a, b)
  a, b = a.to_i, b.to_i # Only works with ints
  return sum_cumulative_recursive(b, a) if a > b
  return a if a == b
  return a + sum_cumulative_recursive(a+1, b)
end

【讨论】:

  • a &gt; b 时不会产生预期的结果。
  • @kardeiz 你能澄清一下吗? a &gt; b 时想要的结果是什么?
  • 根据发帖人的代码,基本上应该是翻转变量,这样你就有了min..max的范围。
【解决方案3】:

这是一种方法。我认为这只是一个练习,因为r 范围内元素的总和当然只是(r.first+r.last)*(f.last-r.first+1)/2

def sum_range(range)
  return nil if range.last < range.first
  case range.size
  when 1 then range.first
  when 2 then range.first + range.last
  else
    range.first + range.last + sum_range(range.first+1..range.last-1)
  end
end

sum_range(14..18)  #=> 80
sum_range(14..14)  #=> 14
sum_range(14..140) #=> 9779
sum_range(14..139) #=> 9639

【讨论】:

  • 什么?它甚至不起作用:undefined method 'size' for 14..18:Range.
  • @Kardeiz,你用词的方式真好。看来Range#size 在 v2.0 中可用。 (我使用的是 v2.1)。对于早期版本,我猜你必须使用range.last-range.first+1
  • @Cary,我没有不尊重的意思。感谢您指出版本差异。
  • @Cary,另外,如果您的答案使用问题的方法签名并解决其他答案已处理的最小/最大问题,那就太好了。
  • @kardeiz,在发布我的解决方案后,我注意到我使用了一个范围作为方法参数,而不是端点,但我得出的结论是差异是如此微不足道,不需要编辑。另外,我认为询问者了解如何使用范围参数可能很有用。是的,我应该处理范围问题(见编辑)。我之前没有这样做,因为我错误地认为(18..14)(例如)会引发异常。 (奇怪的是,(18..14).size =&gt; 0)。)在这种情况下,我现在返回 nil,但本可以反转范围。
【解决方案4】:

另一种解决方案是使用前端调用来修复乱序参数,然后使用私有递归后端来执行实际工作。我发现一旦确定参数是干净的,这对于避免重复检查参数很有用。

def sum_cumulative_recursive(a, b)
  a, b = b, a if b < a
  _worker_bee_(a, b)
end

private
def _worker_bee_(a, b)
  a < b ? (a + _worker_bee_(a+1,b-1) + b) : a == b ? a : 0
end

此变体通过从两端求和将堆栈需求减半。

如果您不喜欢这种方法和/或您真的想减少堆栈大小:

def sum_cumulative_recursive(a, b)
  if a < b 
    mid = (a + b) / 2
    sum_cumulative_recursive(a, mid) + sum_cumulative_recursive(mid+1, b)
  elsif a == b
    a
  else
    sum_cumulative_recursive(b, a)
  end
end

这应该保持堆栈大小为 O(log |b-a|)。

【讨论】:

    猜你喜欢
    • 2013-12-25
    • 2015-09-07
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-18
    • 2021-08-01
    相关资源
    最近更新 更多