Ruby以递归方式计算端点的累积和?

时间:2014-06-09 17:01:38

标签: ruby recursion

给出两个数字,比如说(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)

我的解决方案适用于&gt; b,和&lt; b,但它不适用于== b。

如何修复此代码以使其有效?

感谢您的时间。

4 个答案:

答案 0 :(得分:0)

我这样做:

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

答案 1 :(得分:0)

这是一种做法。我认为这只是一个练习,因为范围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

答案 2 :(得分:0)

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错误......

答案 3 :(得分:0)

另一种解决方案是使用前端调用来修复无序参数,然后使用私有递归后端来完成实际工作。我发现,一旦你确定它们是干净的,就可以避免重复检查参数。

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 |)。