Arrays 如何实现对哈希数组的所有可能修改
我有一个哈希数组,如下所示:Arrays 如何实现对哈希数组的所有可能修改,arrays,ruby,algorithm,Arrays,Ruby,Algorithm,我有一个哈希数组,如下所示: initial_tasks_groups = [ [{task: 'Cut Tree', score: 25}, {task: 'Walk Dog', score: 10}], [{task: 'Clean House', score: 10}, {task: 'Wash Floor', score: 10}, {task: 'Call Uncle Ben', score: 15}], [{task: 'Wash Giraffe', score: 15
initial_tasks_groups = [
[{task: 'Cut Tree', score: 25}, {task: 'Walk Dog', score: 10}],
[{task: 'Clean House', score: 10}, {task: 'Wash Floor', score: 10}, {task: 'Call Uncle Ben', score: 15}],
[{task: 'Wash Giraffe', score: 15}, {task: 'Burn House', score: 19}]
]
我将交换[sic]等效任务称为:用另一个子数组中的一个或多个哈希替换子数组中的一个或多个哈希的操作,以使哈希值的:score
值之和不改变。例如,'Walk dog'
任务可以替换为'Clean House'
任务(两者的得分均为10)。或者,'cuttree'
(25)可以替换为'Walk Dog'
(10)和'Call叔叔Ben'
(15)
我将等效组合称为通过交换初始任务组中的[sic]等效任务创建的数组
我的目标是找到所有可能的等效组合。最终结果应该是如下所示的数组:
all_possible_combinaisons = [
initial_tasks_groups,
[
[{task: 'Cut Tree', score: 25}, {task: 'Clean House', score: 10}],
[{task: 'Walk Dog', score: 10}, {task: 'Wash Floor', score: 10}, {task: 'Call Uncle Ben', score: 15}],
[{task: 'Wash Giraffe', score: 15}, {task: 'Burn House', score: 19}]
],
[
[{task: 'Cut Tree', score: 25}, {task: 'Clean House', score: 10}],
[{task: 'Walk Dog', score: 10}, {task: 'Wash Floor', score: 10}, {task: 'Wash Giraffe', score: 15}],
[{task: 'Call Uncle Ben', score: 15}, {task: 'Burn House', score: 19}]
],
...
]
有没有干净的方法可以做到这一点?这对于少量到中等数量的任务来说相对容易。首先,让我们从散列的初始数组中准备一些数据
input = [
[{task: 'Cut Tree', score: 25},
{task: 'Walk Dog', score: 10}],
[{task: 'Clean House', score: 10},
{task: 'Wash Floor', score: 10},
{task: 'Call Uncle Ben', score: 15}],
[{task: 'Wash Giraffe', score: 15},
{task: 'Burn House', score: 19}]]
目标是洗牌任务,使每个数组的分数保持不变。因此,让我们从收集目标开始:
goals = input.map { |a| a.reduce(0) { |acc, h| acc + h[:score] } }
#⇒ [35, 35, 34]
现在让我们准备过滤函数:
all = input.flatten
filter =
lambda do |sum, already_taken = []|
(1..all.length).flat_map do |i|
all.combination(i).reject do |a|
a.any? { |h| already_taken.include?(h) }
end.select do |a|
a.reduce(0) { |acc, h| acc + h[:score] } == sum
end
end
end
剩下的唯一一件事就是生成所有组合,并在不重复任务的情况下选择这些组合:
goals.
map(&filter).
reduce(&:product).
select do |a|
tasks = a.flatten
tasks.uniq.size == tasks.size
end
上述结果有12种可能的组合,但它们有重复。通过对元素进行排序和检查DUP,可以很容易地筛选出它们。我将把最后一项任务作为家庭作业留给你。代码
require 'set'
def generate_equal_score_permutations(initial_tasks_groups)
all_hashes = initial_tasks_groups.flatten
scores_count = compute_scores_count(all_hashes)
combos = generate_combos(all_hashes, scores_count)
first, *rest = generate_permutations(scores_map, combos)
keys = scores_count.keys
first.product(*rest).
map do |p|
h = keys.zip(p.map(&:to_enum)).to_h
initial_tasks_groups.map do |arr|
arr.flat_map { |g| h[g[:score]].next }
end
end
end
示例
initial_tasks_groups = [
[{task: 'Cut Tree', score: 25}, {task: 'Walk Dog', score: 10}],
[{task: 'Clean House', score: 10}, {task: 'Wash Floor', score: 10},
{task: 'Call Uncle Ben', score: 15}],
[{task: 'Wash Giraffe', score: 15}, {task: 'Burn House', score: 19}]
]
解释
返回值(数组)包含84个元素,每个元素都是初始任务组元素的修改排列。数字84并不奇怪,因为我们可以很容易地计算出来
在初始任务组中有1、3、2和1个hashh
,其中h[:score]
分别等于25、10、15和19。哈希h=initial_tasks\u group[0][0]
(具有h[:score]
equal25
)可以由(自身或)分数为10的三个哈希中的一个和分数为15的两个哈希中的一个替换。因此,分数为25的散列可以出现7种方式。因此,排列的数量是7*3*2*1#=>84
对于初始任务组的给定值,步骤如下
all_hashes = initial_tasks_groups.flatten
#=> [{:task=>"Cut Tree", :score=>25}, {:task=>"Walk Dog", :score=>10},
# {:task=>"Clean House", :score=>10}, {:task=>"Wash Floor", :score=>10},
# {:task=>"Call Uncle Ben", :score=>15}, {:task=>"Wash Giraffe", :score=>15},
# {:task=>"Burn House", :score=>19}]
h = keys.zip(p.map(&:to_enum)).to_h
#=> {25=>#<Enumerator: [[{:task=>"Walk Dog", :score=>10},
# {:task=>"Wash Giraffe", :score=>15}]]:each>,
# 10=>#<Enumerator: [[{:task=>"Wash Floor", :score=>10}],
# [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}]]:each>,
# 15=>#<Enumerator: [[{:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Giraffe", :score=>15}]]:each>,
# 19=>#<Enumerator: [[{:task=>"Burn House", :score=>19}]]:each>}
接下来,我们需要知道所有\u hash
中有多少个hash分别具有:score
的四个值
scores_count = compute_scores_count(all_hashes)
#=> {25=>1, 10=>3, 15=>2, 19=>1}
combos = generate_combos(all_hashes, scores_count)
#=> {25=>[
# [{:task=>"Cut Tree", :score=>25}],
# [{:task=>"Walk Dog", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15}],
# [{:task=>"Clean House", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Clean House", :score=>10}, {:task=>"Wash Giraffe", :score=>15}],
# [{:task=>"Wash Floor", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Floor", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]
# ],
# 10=>[
# [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}],
# [{:task=>"Wash Floor", :score=>10}]
# ],
# 15=>[
# [{:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Giraffe", :score=>15}]
# ],
# 19=>[
# [{:task=>"Burn House", :score=>19}]
# ]
# }
first, *rest = generate_permutations(scores_map, combos)
first
#=> [
# [[{:task=>"Cut Tree", :score=>25}]],
# [[{:task=>"Walk Dog", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}]],
# [[{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Clean House", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}]],
# [[{:task=>"Clean House", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Wash Floor", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}]],
# [[{:task=>"Wash Floor", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]]
# ]
见和
计算组合的第一步是计算以下内容
max_group_size(scores_count)
#=> 2
我们知道分数25
等于分数10
和15
之和。max\u group\u size
的返回值告诉我们没有值:score
(比如说25)等于:score
的两个以上其他值之和。这减少了:score
的值组合的数量,我们需要检查它们的总和
现在我们需要为:score
的4个值中的每一个设置一个排列数组
scores_count = compute_scores_count(all_hashes)
#=> {25=>1, 10=>3, 15=>2, 19=>1}
combos = generate_combos(all_hashes, scores_count)
#=> {25=>[
# [{:task=>"Cut Tree", :score=>25}],
# [{:task=>"Walk Dog", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15}],
# [{:task=>"Clean House", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Clean House", :score=>10}, {:task=>"Wash Giraffe", :score=>15}],
# [{:task=>"Wash Floor", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Floor", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]
# ],
# 10=>[
# [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}],
# [{:task=>"Wash Floor", :score=>10}]
# ],
# 15=>[
# [{:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Giraffe", :score=>15}]
# ],
# 19=>[
# [{:task=>"Burn House", :score=>19}]
# ]
# }
first, *rest = generate_permutations(scores_map, combos)
first
#=> [
# [[{:task=>"Cut Tree", :score=>25}]],
# [[{:task=>"Walk Dog", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}]],
# [[{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Clean House", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}]],
# [[{:task=>"Clean House", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Wash Floor", :score=>10}, {:task=>"Call Uncle Ben", :score=>15}]],
# [[{:task=>"Wash Floor", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]]
# ]
看
接下来,我们使用计算一个数组,该数组首先将的每个元素与rest
的每个元素配对
keys = scores_count.keys
#=> [25, 10, 15, 19]
a = first.product(*rest)\
我们将只看一下a
的84个元素中的一个
p = a[32]
#=> [
# [[{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Wash Floor", :score=>10}], [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}]],
# [[{:task=>"Call Uncle Ben", :score=>15}], [{:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Burn House", :score=>19}]]
# ]
置换的其余步骤如下所示
all_hashes = initial_tasks_groups.flatten
#=> [{:task=>"Cut Tree", :score=>25}, {:task=>"Walk Dog", :score=>10},
# {:task=>"Clean House", :score=>10}, {:task=>"Wash Floor", :score=>10},
# {:task=>"Call Uncle Ben", :score=>15}, {:task=>"Wash Giraffe", :score=>15},
# {:task=>"Burn House", :score=>19}]
h = keys.zip(p.map(&:to_enum)).to_h
#=> {25=>#<Enumerator: [[{:task=>"Walk Dog", :score=>10},
# {:task=>"Wash Giraffe", :score=>15}]]:each>,
# 10=>#<Enumerator: [[{:task=>"Wash Floor", :score=>10}],
# [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}]]:each>,
# 15=>#<Enumerator: [[{:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Giraffe", :score=>15}]]:each>,
# 19=>#<Enumerator: [[{:task=>"Burn House", :score=>19}]]:each>}
请参阅和。您实施的不干净的方法是什么?我认为没有干净的方法。如果你需要一个解决方案来达到同样的效果(不干净的方式),我可以研究一下。考虑到所有可能的组合[2]=初始任务组[2]
,为什么初始任务组的所有元素不都是所有可能的组合的成员呢?你的问题不清楚。此外,在给出示例时,所有内容都应该是Ruby对象。首先,不要说“等等”!我是ruby(和stackoverflow)的新手,如果问题不清楚,我深表歉意。问题的“干净”部分是因为我有一些非常混乱的东西,并且知道它不可能是正确的答案@CarySwoveland,所有可能的组合[2]都不等于最初的任务组[2],但我意识到我的问题没有很好的表述。我花了一些时间才理解(我是新来的!)。我想我现在理解了代码,但我只是不清楚“已采取”数组。你什么时候给它分配任务?那是我的作业吗?:)非常感谢你的回答!哦,对不起,这是剩菜。请随意删除它。
keys = scores_count.keys
#=> [25, 10, 15, 19]
a = first.product(*rest)\
p = a[32]
#=> [
# [[{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Wash Floor", :score=>10}], [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}]],
# [[{:task=>"Call Uncle Ben", :score=>15}], [{:task=>"Wash Giraffe", :score=>15}]],
# [[{:task=>"Burn House", :score=>19}]]
# ]
h = keys.zip(p.map(&:to_enum)).to_h
#=> {25=>#<Enumerator: [[{:task=>"Walk Dog", :score=>10},
# {:task=>"Wash Giraffe", :score=>15}]]:each>,
# 10=>#<Enumerator: [[{:task=>"Wash Floor", :score=>10}],
# [{:task=>"Walk Dog", :score=>10}],
# [{:task=>"Clean House", :score=>10}]]:each>,
# 15=>#<Enumerator: [[{:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Giraffe", :score=>15}]]:each>,
# 19=>#<Enumerator: [[{:task=>"Burn House", :score=>19}]]:each>}
initial_tasks_groups.map do |arr|
arr.flat_map { |g| h[g[:score]].next }
end
#=> [
# [{:task=>"Walk Dog", :score=>10}, {:task=>"Wash Giraffe", :score=>15},
# {:task=>"Wash Floor", :score=>10}],
# [{:task=>"Walk Dog", :score=>10}, {:task=>"Clean House", :score=>10},
# {:task=>"Call Uncle Ben", :score=>15}],
# [{:task=>"Wash Giraffe", :score=>15}, {:task=>"Burn House", :score=>19}]