我正在从一个网站上关注Ruby练习题,我完全坚持找出这个问题的解决方案。基本上,给定函数has_sum?(val, arr)
,如果数组中的任何数字组合(第二个参数)可以加在一起等于第一个参数,则返回true,否则返回false。所以:
has_sum?(5, [1, 2, 3, 4]) # true
has_sum?(5, [1, 2, 6]) # false
我完全陷入困境并且不太确定如何实现这一目标......这就是我到目前为止所拥有的。
def has_sum?(val, arr)
arr.each_with_index do |idx, v|
# ??? no idea what to do here except add the current num to the next in the list
end
end
非常感谢任何帮助 - 谢谢!
答案 0 :(得分:5)
当存在任何长度的子集时,数组可以产生总和:
def has_sum?(val, arr)
(arr.size + 1).times
.flat_map { |i| arr.combination(i).to_a }
.any? { |s| s.inject(:+) == val }
end
has_sum?(5, [5])
# => true
has_sum?(5, [1, 2, 3])
# => true
has_sum?(5, [1, 1, 1, 1, 1, 1])
# => true
has_sum?(5, [1, 2, 7])
# => false
这不是很有效,因为它在测试之前会产生所有可能性。这应该更快终止:
def has_sum?(val, arr)
(arr.size + 1).times.any? { |i|
arr.combination(i).any? { |s| s.inject(:+) == val }
}
end
更有效的是,递归实现,其中一个空数组的总和为零(并且has_sum(nonzero, [])
应返回false);对于一个更大的数组,我们弹出它的头部,如果我们计算或不计算head元素,看看数组其余部分的总和是否正常。在这里,我们不会一遍又一遍地对整个数组进行无用的求和:
def has_sum?(val, arr)
if arr.empty?
val.zero?
else
first, *rest = arr
has_sum?(val, rest) || has_sum?(val - first, rest)
end
end
答案 1 :(得分:1)
此解决方案采用动态编程。我假设已经从数组中删除了零。如果数组中的所有数字都是正数,我们也可以删除大于目标总和的元素。
<强>代码强>
def sum_to_target(arr, target)
h = arr.each_index.with_object({}) do |i,h|
v = arr[i]
h.keys.each do |n|
unless h.key?(n+v) # || (n+v > target)
h[n+v] = v
return reconstruct(h, target) if n+v == target
end
end
h[v] = v unless h.key?(v)
return reconstruct(h, target) if v == target
end
nil
end
def reconstruct(h, target)
a = []
loop do
i = h[target]
a.unshift i
target -= i
return a if target == 0
end
a
end
如果arr
仅包含正值,则可以进一步提高效率。 1
示例
#1
sum_to_target [2,4,7,2], 8
#=> [2, 4, 2]
#2
arr = [64, 18, 64, 6, 39, 51, 87, 62, 78, 62, 49, 86, 35, 57, 40, 15, 74, 10, 8, 7]
a = sum_to_target(arr, 461)
#=> [64, 18, 39, 51, 87, 62, 78, 62]
让我们检查一下。
a.reduce(:+)
#=> 461
#3
a = sum_to_target([-64, 18, 64, -6, 39, 51, -87, 62, -78, 62, 49, 86, 35, 57,
40, 15, -74, 10, -8, -7], 190)
#=> [18, 64, -6, 39, 51, -87, 62, 49]
a.reduce(:+)
#=> 190
#4
arr = 1_000.times.map { rand 1..5_000 }
#=> [3471, 1891, 4257, 2265, 832, 1060, 3961, 875, 614, 2308, 2240, 3286,
# ...
# 521, 1316, 1986, 4099, 1398, 3803, 4498, 4607, 2262, 3941, 4367]
arr
是一个包含1,000个元素的数组,每个元素都是1到5,000之间的随机数。
answer = arr.sample(500)
#=> [3469, 2957, 1542, 950, 4765, 3126, 3602, 755, 4132, 4281, 2374,
# ...
# 427, 4238, 4397, 2717, 912, 1690, 3626, 169, 3607, 4084, 3161]
answer
是来自arr
的500个元素的数组,无需替换即可进行采样。
target = answer.reduce(:+)
#=> 1_226_020
target
是answer
元素的总和。我们现在将在arr
中搜索总计为1,226,020
(answer
为一个此类集合)的元素集合。
require 'time'
t = Time.now
#=> 2016-12-12 23:00:51 -0800
a = sum_to_target(arr, target)
#=> [3471, 1891, 4257, 2265, 832, 1060, 3961, 875, 614, 2308, 2240, 3286,
# ...
# 3616, 4150, 3222, 3896, 631, 2806, 1932, 3244, 2430, 1443, 1452]
请注意a != answer
(这并不奇怪)。
a.reduce(:+)
#=> 1226020
(Time.now-t).to_i
#=> 60 seconds
对于最后一个示例,使用Array#combination的方法必须涉及
(1..arr.size).reduce(0) { |t,i| t + arr.combination(i).size }.to_f
#~> 1.07+301
的组合。
<强>解释强>
让
arr = [2,4,7,2]
target = 8
假设我们暂时重新定义reconstruct
以返回传递给它的哈希值。
def reconstruct(h, target)
h
end
然后我们获得以下内容:
h = sum_to_target(arr, target)
#=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7, 8=>2}
h
定义如下。
给定一组非零整数arr
和一个数字n
,如果n
是h
的键,则存在一个数组{{1包含来自a
的元素,顺序相同,以使arr
的元素总和为a
,n
的最后一个元素等于a
。
我们现在使用h[n]
(在“代码”部分中定义)构造一个数组reconstruct
,它将包含来自answer
的元素(没有重复元素),总和为{ {1}}。
arr
最初,target
初始化数组reconstruct(h, target) #=> [2, 4, 2]
,它将构建并返回:
reconstruct
answer
将始终包含等于target(answer = []
)的键。在h
我们得出结论,8
的最后一个元素等于h[8] #=> 2
,所以我们执行
answer
现在的问题是找到2
总和为answer.unshift(2) #=> [2]
的元素数组。作为arr
,我们得出结论,8 - 2 #=> 6
中我们刚添加的h[6] #=> 4
之前的元素是answer
:
2
我们现在需要4
更多,总共answer.unshift(4) #=> [4, 2]
。正如8-2-4 #=> 2
我们执行
target
自h[2] #=> 2
完成后,我们就会返回answer.unshift(2) #=> [2, 4, 2]
。
请注意,8-2-4-2 #=> 0
位于answer
中的最后4
之前,而2
位于arr
中的2
之前。构建4
的方式可确保arr
的元素始终以这种方式排序。
现在考虑如何构建h
。首先,
answer
作为h
,我们得出的结论是,仅使用h = {}
的第一个元素,我们可以得出的结论是:
arr[0] #=> 2
arr
没有等于h[2] = 2
h #=> {2=>2}
(h
)的键,所以我们继续。现在考虑target
。仅使用8
的前两个元素,我们可以得出以下结论:
arr[1] #=> 4
由于arr
没有键h[2+4] = 4
h #=> {2=>2, 6=>4}
,
h
4
仍然没有等于h[4] = 4
h #=> {2=>2, 6=>4, 4=>4}
(h
)的密钥,因此我们按下并检查target
。仅使用8
的前三个元素,我们得出以下结论:
arr[2] #=> 7
由于arr
没有密钥h[2+7] = 7
h[6+7] = 7
h[4+7] = 7
h #=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7}
:
h
我们向7
添加了四个元素,但由于h[7] = 7
h #=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7}
仅包含正数,因此具有键h
,arr
和9
的元素无关紧要
由于13
仍然没有等于11
(h
)的密钥,我们会检查target
中的下一个元素:8
。仅使用arr
的前四个元素,我们得出以下结论:
arr[3] #=> 2
此处我们停止,因为arr
。
h[4+2] = 2
h[6+2] = 2
请注意,由于6+2 == target #=> true
已有密钥h #=> {2=>2, 6=>2, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7, 8=>2}
,因此我们未计算h[2+2] = 2
。此外,如果h
包含其他元素,我们仍然会在此时终止哈希的构造。
如果我们修改代码以利用4
仅包含正值的事实,那么最终的哈希就是:
arr
如果仍然不清楚,那么使用包含的arr
语句运行此示例的代码可能会有所帮助(例如h #=> {2=>2, 6=>2, 4=>4, 7=>7, 8=>2}
后面的puts
puts "i=#{i}, h=#{h}, v=#{v}"
}, 等等)。
1如果已知数组不包含负数元素,则行v = arr[i]
可以更改为sum_to_target
。 (这样做会将解决方案时间缩短4秒。)还可以计算unless h.key?(n+v)
,然后使该行以unless h.key?(n+v) || (n+v > target)
为条件。
答案 2 :(得分:0)
我会做嵌套循环。
for x = 0 to length of array
for y = x + 1 to length of array
if number at x + number at y = sum return true
return false
基本上它会检查每个数字的总和以及后面的每个数字。
编辑:这一次只能汇总2个数字。如果你想能够总结任何数量的数字,这将不起作用。