执行超时(12000毫秒):如何优化此简单的kata以使其运行更快?

时间:2019-07-15 21:45:54

标签: ruby

我很长时间休息后正在练习我的编码印章,然后在CodeWars上碰到了这个kata

在数组中输入数字后,返回其各部分的和。例如:

f1()

我的解决方案解决了kata问题,但是一旦我到达大约30,000+的数组,我的解决方案将花费很长时间解决。

因此,我的问题是向社区提出,我将如何尝试使其更快地发展。我知道递归通常很慢,并且for循环及其变体通常足以完成工作。如果失败了怎么办?有什么方法可以使我的代码更快?

我正在寻找一些建议和示例(如果有人)。欣赏输入。谢谢。

3 个答案:

答案 0 :(得分:3)

def parts_sums(ls)
  ls.each_with_object([ls.sum]) { |n,arr| arr << arr.last - n }
end

parts_sums([0, 1, 3, 6, 10])
  #=> [20, 20, 19, 16, 10, 0] 

答案 1 :(得分:2)

该代码的问题在于,您在循环的每次迭代中都执行inject,这不必要地很慢。

在任何循环之外,您只需要对数组的元素求和一次。一旦有了该总和,就可以遍历数组的元素,并从当前总和中执行恒定的时间减法,然后将其推入sums数组中。

def part_sums(ls)
  sum = ls.inject(:+)
  sums = [sum]
  ls.each do |val|
    sum -= val
    sums << sum
  end
  sums
end

如果您使用each迭代器遍历数组或保留一个计数器并使用while循环,也无需进行移位。

答案 2 :(得分:0)

此功能的版本运行速度更快:

def parts_sums_2(ls)
 sums = []
 last_sum = 0
 (ls.length - 1).downto(0).each do |i|
   last_sum += ls[i]
   sums.prepend last_sum
 end
 sums + [0]
end

此处的关键是向后遍历数组 -从最小的和(仅最后一个元素)开始。随后的每个步骤都将一个索引移到开头,并将该值添加到先前的总和中。

由于问题陈述要求您每一步都要 shift ,因此即使开始时要计算的结果总和也必须在开始时最大。这就是为什么我的代码使用 prepend 而不是push的原因。

这是O(N)的时间复杂度,而不是O(N ^ 2),这是一个数量级的差异。

使用100_000个输入,您的原始功能花了7.040443秒,而我的这里花了0.000008秒

通常,您还应尝试避免使方法的输入发生变化(就像您使用 shift 一样)。