(很抱歉,这个问题不是重复的问题,重复的问题是一个错误的帖子,已被删除)
比方说,我有人们选择的数据:
data = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple", "pear"],
"pat"=>["apple", "pear","banana"]
}}
数据意味着jaime不在乎获取苹果或香蕉。现在,我要进行公平分配,以便每个人都可以得到一个在其偏好设置范围内的水果,但是哪个水果都没关系。
还有其他条件:
data = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple", "pear"],
"pat"=>["apple", "pear","banana","orange"]
}}
outcome = {
"jaime"=>["apple"],
"taylor"=>["pear"],
"pat"=>["banana","orange"]
}
data = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple", "pear"],
"pat"=>["apple", "banana"]
}}
outcome = {
"jaime"=>["apple"],
"taylor"=>["pear"],
"pat"=>["banana"]
}
data = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple", "banana"],
"pat"=>["apple", "banana"]
}}
outcome = {
"jaime"=>["apple"],
"taylor"=>["banana"],
"pat"=>[]
}
我开始集思广益一些代码
data = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple", "pear"],
"pat"=>["apple", "pear","banana"]
}}
fruit_avail = ["apple","banana","pear"]
result = {"allocation"=>{},"will_not_get_anything"=>[]}
# helper array, contains people that are "done" being allocated
finished = []
fruit_avail.each do |fruit|
unfinished = data["choices"].reject { |person,options| finished.include? person }
# helper hash, contains people who have yet to be allocated (opposite of finished)
first_person_who_has_fruit_choice = unfinished.first { |person,options| v.include? fruit }[0]
# this is the "someone". since i use .first, this will bias toward the first person with the fruit preference in the choices data getting it. In other words, in the absense of enough fruit, the last person will be empty handed, in the presence of too much fruit, the last person will also have the extra choice
unfinished.each do |person, options|
if first_person_who_has_fruit_choice == person
result["allocation"][person] = [fruit]
finished << person
else
updated_options = result["allocation"][person].present? result["allocation"][person] : options
# helper variable, gets the latest options for this person (which may have been trimmed due to earlier allocations
remaining_options = updated_options - [fruit]
result["allocation"][person] = remaining_options
result["will_not_get_anything"] << person if remaining_options.blank?
end
end
end
但是上面的方法并没有捕获数据如下的情况:
data = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple"],
"pat"=>["apple", "pear"]
}}
由于该代码仅在列表中起作用,因此将产生以下结果:
outcome = {
"jaime"=>["apple"],
"taylor"=>[],
"pat"=>["pear"]
}
实际正确的结果应该是:
outcome = {
"jaime"=>["banana"],
"taylor"=>["apple"],
"pat"=>["pear"]
}
任何想法或建议都会受到赞赏。
答案 0 :(得分:0)
让我们逐步进行。
choices = {"choices"=> {
"jaime"=>["apple", "banana"],
"taylor"=>["apple"],
"pat"=>["apple", "pear"]
}}['choices']
现在让我们建立水果=>人们的反向哈希:
fruits_to_ppl =
choices.each_with_object(Hash.new { |h, k| h[k] = [] }) do |(k, vs), acc|
vs.each { |v| acc[v] << k }
end.sort_by { |_, v| v.size }.to_h
#⇒ {"banana"=>["jaime"], "pear"=>["pat"], "apple"=>["jaime", "taylor", "pat"]}
此外,让我们按偏好列表的大小对ppl进行排序:
sorted = choices.sort_by { |_, v| v.size }.map(&:first)
好的,我们准备好了。
distribution =
fruits_to_ppl.each_with_object(Hash.new { |h, k| h[k] = [] }) do |(f, ppl), acc|
who = sorted.detect { |s| ppl.detect(&s.method(:==)) && acc[s].empty? }
acc[who] = f if who
end
#⇒ {"jaime"=>"banana", "pat"=>"pear", "taylor"=>"apple"}
该解决方案是不完整的,可能需要对桶中剩余的物品进行明确处理,但这是完成任务的良好起点。
答案 1 :(得分:0)
这是解决我认为是NP完全问题的蛮力方法。这样可以确保分配给首选水果的人数最大化。
代码
def assign_fruit(preferences, basket)
prefs = preferences.values
best = [nil, nil, nil]
best_count = 0
basket.permutation(prefs.size) do |perm|
arr = prefs.zip(perm).map { |pref, p|
pref.include?(p) ? p : nil }
sz = arr.compact.size
if sz > best_count
best = arr
break if sz == prefs.size
best_count = sz
end
end
preferences.transform_values { best.shift }
end
示例
preferences = {
"jaime"=>["apple", "banana"],
"taylor"=>["apple", "pear"],
"pat"=>["apple", "pear", "banana", "orange"]
}
assign_fruit(preferences, ["pear", "banana", "plum"])
#=> {"jaime"=>"banana", "taylor"=>"pear", "pat"=>nil}
assign_fruit(preferences, ["pear", "banana", "apple", "plum"])
#=> {"jaime"=>"banana", "taylor"=>"pear", "pat"=>"apple"}
assign_fruit(preferences, ["pear", "banana", "orange"])
#=> {"jaime"=>"banana", "taylor"=>"pear", "pat"=>"orange"}
assign_fruit(preferences, ["orange", "orange", "orange"])
#=> {"jaime"=>nil, "taylor"=>nil, "pat"=>"orange"}
此方法允许水果篮中的水果计数超过每个水果中的一个。
说明
如果prefences
如示例中所述,并且
basket = ["pear", "banana", "plum", "fig"]
然后
prefs = preferences.values
#=> [["apple", "banana"], ["apple", "pear"],
# ["apple", "pear", "banana", "orange"]]
enum = basket.permutation(prefs.size)
#=> #<Enumerator: ["pear", "banana", "plum",
# "fig"]:permutation(3)>
enum.size
#=> 24
我们可以看到enum
元素将通过将enum
转换为数组而生成并传递给块:
enum.to_a
#=> [["pear", "banana", "plum"], ["pear", "banana", "fig"],
# ["pear", "plum", "banana"], ["pear", "plum", "fig"],
# ["pear", "fig", "banana"], ["pear", "fig", "plum"],
# ...
# ["fig", "plum", "pear"], ["fig", "plum", "banana"]]
生成并传递到块的第一个元素是:
perm = enum.next
#=> ["pear", "banana", "plum"]
然后我们计算:
pairs = prefs.zip(perm)
#=> [[["apple", "banana"], "pear"],
# [["apple", "pear"], "banana"],
# [["apple", "pear", "banana", "orange"], "plum"]]
然后将其映射到:
pairs.map { |pref, p| pref.include?(p) ? p : nil }
#=> [nil, nil, nil]
表明没有人喜欢吃水果。
考虑第二个例子。时间:
perm = ["pear", "apple", "banana"]
我们获得:
pairs = prefs.zip(perm)
#=> [[["apple", "banana"], "pear"],
# [["apple", "pear"], "apple"],
# [["apple", "pear", "banana", "orange"], "banana"]]
pairs.map { |pref, p| pref.include?(p) ? p : nil }
#=> [nil, "apple", "banana"]
表明两个人应该感到高兴。对于考虑的每个排列,将获得首选果实的人数与迄今为止获得的最佳果实进行比较。如果该数字大于到目前为止的最佳分配,则该分配将成为迄今为止的最佳分配。如果每个人都能得到喜欢的水果,我们就可以摆脱困境。
答案 2 :(得分:0)
这是一个冗长的解决方案,不仅为了展示基本思想而进行了优化。
我希望代码能自我解释,或者稍后再添加解释。
基本思想是跟踪一个人收到多少水果,并对其进行每次排序,以优先于拥有较少水果的人。
data = {"choices"=> {
"jaime"=>["apple", "banana", "kiwi"],
"taylor"=>["apple", "pear","peach"],
"pat"=>["apple", "pear","banana","peach"]
}}
fruit_avail = ["apple","banana","pear","peach","melon"]
# setup
people_fruits_request = data['choices'].transform_values { |v| v & fruit_avail }.sort_by{ |_, v| v.size }.to_h
people_fruits_allocation = people_fruits_request.transform_values { |v| v = [] }
people_fruits_count = people_fruits_request.transform_values { |v| v = 0 }
fruit_avail_requested = fruit_avail & people_fruits_request.values.flatten.uniq
# calculation
fruit_avail_requested.each do |fruit|
people_requesting = people_fruits_request.select { |_, fruits| fruits.include? fruit }.keys
person = (people_fruits_count.sort_by { |k,v| v}.map(&:first) & people_requesting).first
people_fruits_allocation[person] << fruit
people_fruits_count[person] += 1
end
people_fruits_allocation
#=> {"jaime"=>["apple"], "taylor"=>["pear", "peach"], "pat"=>["banana"]}
fruits_left_in_the_basket = fruit_avail - fruit_avail_requested
#=> ["melon"]