我很长时间休息后正在练习我的编码印章,然后在CodeWars上碰到了这个kata
在数组中输入数字后,返回其各部分的和。例如:
f1()
我的解决方案解决了kata问题,但是一旦我到达大约30,000+的数组,我的解决方案将花费很长时间解决。
因此,我的问题是向社区提出,我将如何尝试使其更快地发展。我知道递归通常很慢,并且for循环及其变体通常足以完成工作。如果失败了怎么办?有什么方法可以使我的代码更快?
我正在寻找一些建议和示例(如果有人)。欣赏输入。谢谢。
答案 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 一样)。