我有一个数字数组,表示某些实体需要完成某些处理的次数:
array = [20, 30, 10, 7, 8, 5]
和一个数字,表示实际完成处理的总次数:
amount_processed = 80
我想构建一个哈希,其键是数组中的数字,其值表示成功处理80的次数。例如:
hash = {}
index = 0
until amount_processed <= 0 || index == array.count
mapped_amount = [array[index],amount_processed].min
hash[array[index]] = mapped_amount
amount_processed -= mapped_amount
index += 1
end
这种情况下的输出是:
{20 => 20, 30 => 30, 10 => 10, 7 => 7, 8 => 8, 5 => 5}
如果是amount_processed = 65
,我会得到:
{20 => 20, 30 => 30, 10 => 10, 7 => 5}
我想映射amount_processed
,以便它总是首选一个映射,其中所有给定的键都用完了。例如,对于amount_processed = 65
,输出应为:
{20 => 20, 30 => 30, 10 => 10, 5 => 5} # skipped 7 and 8 entirely
如果有不同的可能输出,则两者都有效,我无动于衷。即,如果amount_processed = 60
,则以下2中的任何一个都是有效的
{20 => 20, 30 => 30, 10 => 10}
{30 => 30, 10 => 10, 7 => 7, 8 => 8, 5 => 5}
成功实现上述成果的代码如下所示
hash = {}
index = 0
size = array.size
until amount_processed <= 0
if index == size * 2
hash["notes"] = "attempting to go beyond 2nd iteration, i.e., there is still amount_processed left but nothing more in the array to map it to"
return hash
elsif index >= size
# we've already looped through everything to find something that fully matches, and still have leftover amounts to be mapped, so now start over, and just go in order to fill in whatever's available
pseudo_index = index - size # allows us to map to original array once index is on second iteration
# was that particular one skipped or not
if hash[array[pseudo_index]].present?
# it wasn't skipped in earlier go around, so skip it NOW
else
mapped_amount = [array[pseudo_index],amount_processed].min
hash[array[pseudo_index]] = mapped_amount
amount_processed -= mapped_amount
end
else
if amount_processed < array[index]
# we don't want a partial map, so just don't, unless it's last resort
else
mapped_amount = [array[index],amount_processed].min
hash[array[index]] = mapped_amount
amount_processed -= mapped_amount
end
end
index += 1
end
return hash
我的代码是否始终有效,因为任何array/amount_processed
组合都可以创建一个哈希,该哈希首先匹配多个完全匹配的数组&#34; as然后创建不完整的匹配?
答案 0 :(得分:1)
简单回答:在某些情况下,您的代码会失败
以amount_processed = 62
为例。一个正确的解决方案是{20,30,7,5},但您的代码将返回{20,30,10}
原因是原始代码部分将选择数组值,如果它小于amount_processed
变量,则会导致选择不必要的数组值。
建议:重新思考子集和问题的逻辑,而不是重用原始的贪婪算法
答案 1 :(得分:0)
正如@iGian在他的回答中指出的那样,可以通过所有组合来找到解决方案,但有时可能会尝试猜测最终数组的大小,所以我创建了以下代码,试图做以一种非常天真的方式:
@amount_processed = 62
@iterations = 0
def found_solution given_array
puts "Found a solution"
puts given_array.inspect
puts given_array.sum == @amount_processed ? "Exact solution" : "Aproximate solution"
puts "After #{@iterations} iterations"
exit 0
end
hash = {}
solution = []
array = [20, 30, 10, 7, 8, 5]
found_solution(array) if array.sum <= @amount_processed
found_solution([array.min]) if array.min >= @amount_processed
min_size_array = 0
array.size.times do |i|
@iterations += 1
min_size_array = i
sum_of_maxs = array.max(min_size_array).sum
found_solution(array.max(min_size_array)) if sum_of_maxs == @amount_processed
break if array.max(min_size_array).sum > @amount_processed
end
max_value_array = array.max
max_size_array = min_size_array
(min_size_array..array.size).each do |i|
@iterations += 1
sum_of_mins = array.min(i).sum
found_solution(array.min(i)) if sum_of_mins == @amount_processed
break if sum_of_mins > @amount_processed
max_size_array = i
end
max_value_in_array = array.max
# Run through all the combinations within the given sizes until a solution is found, including iterations for
# non exact solutions, which can at most be as much as 'max_value_in_array' away from the amount_processed
(0..max_value_in_array).each do |difference_to_solution|
(min_size_array..max_size_array).each do |size|
array.combination(size).each do |current_combination|
@iterations += 1
if current_combination.sum == (@amount_processed - difference_to_solution)
found_solution(current_combination) unless difference_to_solution > 0
# There is a difference, so we need to find the min_value that is not in the current_combination
# and add it to the approximate solution
duplicate_array = array.dup
current_combination.each do |x|
duplicate_array.slice!(duplicate_array.index(x))
end
min_value_not_in_current_combination = duplicate_array.min
found_solution(current_combination + [min_value_not_in_current_combination])
end
end
end
end
答案 2 :(得分:0)
您的问题与Knapsack problem有一些相似之处,正如其他人所说,它可能没有最佳的算法解决方案。您的“适合”功能有一些额外的要求,似乎使它更具挑战性。以不同的顺序处理数组元素可能会产生更优化的解决方案,因此我认为您必须尝试所有这些并选择最符合您标准的解决方案。
我的解决方案试图通过所有排列来强制推出最佳解决方案。它利用Ruby的哈希标识(例如{ 1=>2, 2=>3 }
等于{ 2=>3, 1=>2 }
)来删除重复的候选解决方案。我尝试将fit函数与您的要求对齐。
def solve(*args)
best_fit(compute_solutions(*args))
end
def best_fit(solutions)
# prefer unique solutions with no partial values
preferred_solutions, alternative_solutions =
solutions.uniq.partition { |solution| solution.all? { |k, v| k == v } }
if preferred_solutions.empty?
# prefer unique solutions with "fullest" partial values
preferred_solutions = alternative_solutions
.group_by { |solution| solution.detect { |k, v| v < k and break k } }
.min_by(&:first)
.last
end
# prefer shortest solution matching above criteria
preferred_solutions
.group_by(&:length)
.min_by(&:first)
.last
.first
end
def compute_solutions(array, *args)
return enum_for(__callee__, array, *args).to_a unless block_given?
array.permutation do |permuted_array|
yield compute_solution(permuted_array, *args)
end
end
def compute_solution(array, amount_processed)
return enum_for(__callee__, array, amount_processed).to_h unless block_given?
array.each do |amount|
break if amount_processed.zero?
value = [amount, amount_processed].min
yield [amount, value]
amount_processed -= value
end
end
p solve([20, 30, 10, 7, 8, 5], 80) == { 20=>20, 30=>30, 10=>10, 7=>7, 8=>8, 5=>5 } # => true
p solve([20, 30, 10, 7, 8, 5], 79) == { 20=>20, 30=>30, 10=>10, 7=>7, 8=>8, 5=>4 } # => true
p solve([20, 30, 10, 7, 8, 5], 65) == { 20=>20, 30=>30, 10=>10, 5=>5 } # => true
p solve([20, 30, 10, 7, 8, 5], 62) == { 20=>20, 30=>30, 7=>7, 5=>5 } # => true
p solve([20, 30, 10, 7, 8, 5], 60) == { 20=>20, 30=>30, 10=>10 } # => true
答案 3 :(得分:-1)
也许这就是你要找的逻辑? 如果没有匹配并且返回一个空数组
,它将通过所有组合array = [20, 30, 10, 7, 8, 5]
amount_processed = 65
go = true
(array.size + 1).times do |n|
combinations = array.combination(n).to_a
combinations.each do |combination|
sum = combination.inject(0){|sum,x| sum + x }
if sum == amount_processed
then
p combination
go = false
end
break if go == false
end
break if go == false
end