检查数组中的数字是否与输入参数匹配

时间:2016-12-13 02:31:25

标签: ruby

我正在从一个网站上关注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

非常感谢任何帮助 - 谢谢!

3 个答案:

答案 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

targetanswer元素的总和。我们现在将在arr中搜索总计为1,226,020answer为一个此类集合)的元素集合。

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,如果nh的键,则存在一个数组{{1包含来自a的元素,顺序相同,以使arr的元素总和为an的最后一个元素等于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} 仅包含正数,因此具有键harr9的元素无关紧要

由于13仍然没有等于11h)的密钥,我们会检查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个数字。如果你想能够总结任何数量的数字,这将不起作用。