【问题标题】:How do I convert my logic to an algorithm in Ruby? (Obtaining a sum of combination of values closest to the target) [duplicate]如何将我的逻辑转换为 Ruby 中的算法? (获得最接近目标的值组合的总和)[重复]
【发布时间】:2013-09-20 06:42:21
【问题描述】:

我需要创建最接近目标值的值的组合。组合必须是 x 个数字,其中 x 由用户定义。该算法将输出最接近用户输入的目标值的组合。我还需要显示算法返回的键(值)。

我认为该算法的工作原理如下:

Target: 575

具有对应键的值:

150 [0] | 75 [1] | 123 [2] | 212 [3] | 23 [4] | 89 [5] | 20 [6]

77 [7] | 39 [8] | 16 [9] | 347 [10] | 512 [11] | 175 [12]

用户想要Groups of: 5 values

该算法现在对整个集合运行 5 个值的组合,并返回最接近目标值 575 的值的总和。

结果

150 [0] + 212 [3] + 23 [4] + 77 [7] + 89 [5] = 551

使用的键是 0、3、4、7 和 5。

我可以使用Arrays#combination(n),但我将无法跟踪密钥。我已经能够想出一个存储“key”=>“int values”的哈希,但我不知道如何想出一个优化的算法来组合存储在哈希中的值。

{0=>"150"}
{1=>"212"}
{2=>"23"}
{3=>"77"}
{4=>"89"}

附:这不是家庭作业。这是一个个人项目,可以放在简历上,在面试中谈论,并学习将我的想法转化为代码。

【问题讨论】:

  • 你确定要处理a combination of sum of values吗?所以你准备了一个数字组合的组合,并计算每个组合的总和?而且,combination [of numbers] closest to a target (number) value 没有意义。一个组合(可能是一个数组)如何在任何意义上“最接近”一个数字?
  • 它是组合的总和。说从最接近目标的集合中的任何 X 值的总和。
  • 你能猜出每个数组中有多少个元素吗?如果像上面所示的元素很少,那么简单的蛮力方法可能就是要走的路。
  • 这取决于用户输入。如果用户可以请求使值接近目标的 10 或 100 个值的总和。

标签: ruby algorithm combinations hash


【解决方案1】:

这样的事情会起作用:

hash = {0 => 150, 1 => 75, 2 => 123, 3 => 212, 4 => 23, 5 => 89, 6 => 20, 7 => 77, 8 => 39, 9 => 16, 10 => 347, 11 => 512, 12 => 175}

# all key combinations of length 5
keys_of_5 = hash.keys.combination(5)
#=> #<Enumerator: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12]:combination(5)>
#   i.e. [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6], ...]

# sum the values for each combination
sums_of_5 = keys_of_5.map { |keys| [keys, hash.values_at(*keys).inject(:+)] }
#=> [[[0, 1, 2, 3, 4], 583], [[0, 1, 2, 3, 5], 649], [[0, 1, 2, 3, 6], 580], ...]

# sort by distance to target
sorted = sums_of_5.sort_by { |keys, sum| (sum - 575).abs }
#=> [[[4, 5, 7, 8, 10], 575], [[0, 4, 8, 9, 10], 575], [[3, 4, 5, 7, 12], 576], ...]

# let's find the nearest ones
nearest = sorted.select { |keys, sum| sum == sorted.first[1] }

# and print 'em
nearest.each do |keys, sum|
  puts keys.map { |key| "%3d [%d]" % [hash[key], key] }.join(" + ") << " = #{sum}"
end

输出

 23 [4] +  89 [5] +  77 [7] +  39 [8] + 347 [10] = 575
150 [0] +  23 [4] +  39 [8] +  16 [9] + 347 [10] = 575

【讨论】:

  • min_by {} 等价于 sort {}.first(并且更易于阅读 IMO)
  • 前者效率更高。
  • @Stefan 小心。 OP改变了问题。但无论如何,您的总和与我的不同,这不应该取决于给出键/索引的方式。我想知道怎么了。
  • 对不起那些家伙。我认为索引对于理解逻辑并不重要,但显然确实如此,因为你们实际上都在用数字进行测试,
  • 我已经更新了我的答案以反映 OP 的编辑并修复了一个错误。
【解决方案2】:

为了跟踪索引,您可以将combination 应用于数组的索引,而不是数组本身。

array = [150, 75, 212, 23, 89, 20, 77, 39, 16, 347, 512, 175]
target = 575
x = 5

closest_indice =
array
.each_index.to_a
.combination(x)
.min_by{|is| (array.values_at(*is).inject(:+) - target).abs}

但是,答案与您声称的不同:

closest_indice # => [0, 3, 7, 8, 9]
array.values_at(*closest_indice) # => [150, 23, 39, 16, 347]
array.values_at(*closest_indice).inject(:+) # => 575

我不明白你为什么有不同的答案。


编辑

注意到我的 Stefan,没有索引 2。为了解决这个问题:

hash = {0 => 150, 1 => 75, 3 => 212, 4 => 23, 5 => 89, 6 => 20, 7 => 77, 8 => 39, 9 => 16, 10 => 347, 11 => 512, 12 => 175}
target = 575
x = 5

closest_keys =
hash
.keys
.combination(x)
.min_by{|is| (hash.values_at(*is).inject(:+) - target).abs}

closest_keys # => [0, 4, 8, 9, 10]
hash.values_at(*closest_indice) # => [150, 23, 39, 16, 347]
hash.values_at(*closest_indice).inject(:+) # => 575

注意此答案适用于开头的问题(即,在 OP 更改问题以添加索引为 2 的元素 123 之前)。

【讨论】:

  • 212 的索引为3,没有索引2
  • 谢谢。我错过了这一点。我其实在想为什么我的回答和你的不一样。
  • 我的回答是基于随机抽取的示例数字。
  • @Stefan:感谢您指出这一点。我加了一个 2。现在是 00:30,我需要一些咖啡。或者睡觉。
猜你喜欢
  • 1970-01-01
  • 2011-03-09
  • 2017-06-12
  • 2021-12-16
  • 1970-01-01
  • 2022-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多