如何解决使用Dijkstra降低成本?
我花了几个小时来回答一个所谓的“轻松”练习问题,并认为我可以提出一个更可靠的问题。希望能得到反馈。
问题仍然存在:
您需要配备一张办公桌,以便一个人一直在这里连续24小时。您可以雇用6个人来这样做,但是您希望将成本降到最低。工作时间小于等于8小时的任何人每小时赚15美元,工作时间大于8小时的任何人每小时赚20美元。可以使用哪种贪婪算法来最小化成本? (例如,确定是否要雇用2个人,每个人要使用12个小时,请雇用1个人来24小时,请3个人来分别雇用8小时,等等)?
所以我知道Dijkstra是一种“贪心”算法,可以让我们使用加权边。
我理解问题的症结所在-我可以使用Dijkstra来找到一套可以最大程度降低24小时轮班费用的套件。 (例如,从顶层节点将子节点分解为“ 1人移位”,“ 2人移位”,“ 3人移位”,“ 4人移位”,“ 5人移位”,“ 6人移位“ ...然后在其他子节点中列出所有组合。边缘的“权重”是成本。我想找到“最便宜的”路径,但是显然这很麻烦,因为我必须列出所有可能的分组(即5个和6个安全警卫)及其费用。
In [4]:
shift = {}
shift["1_guard"] = {}
shift["2_guards"] = {}
shift["3_guards"] = {}
shift["4_guards"] = {}
shift["5_guards"] = {}
shift["6_guards"] = {}
#at each step we're doing a brute force attack to figure out ok ... if we have x guards,whats the breakdown of the total costs possible?
shift["1_guard"]["Shifts_Filled"] = (24 * 20)
shift["2_guards"]["1_hour_23_hours"] = ((15 * 1)+ (20 * 23))
shift["2_guards"]["2_hour_22_hours"] = ((15 * 2)+ (20 * 22))
shift["2_guards"]["3_hour_21_hours"] = ((15 * 3)+ (20 * 21))
shift["2_guards"]["4_hour_20_hours"] = ((15 * 4)+ (20 * 20))
shift["2_guards"]["5_hour_19_hours"] = ((15 * 5)+ (20 * 19))
shift["2_guards"]["6_hour_18_hours"] = ((15 * 6)+ (20 * 18))
shift["2_guards"]["7_hour_17_hours"] = ((15 * 7)+ (20 * 17))
shift["2_guards"]["8_hour_16_hours"] = ((15 * 8)+ (20 * 16))
shift["2_guards"]["9_hour_15_hours"] = ((15 * 9)+ (20 * 15))
shift["2_guards"]["10_hour_14_hours"] = ((15 * 10)+ (20 * 14))
shift["2_guards"]["11_hour_13_hours"] = ((15 * 11)+ (20 * 13))
shift["2_guards"]["12_hour_12_hours"] = ((15 * 12)+ (20 * 12))
#you dont need to do more than this because it will be one of these combos (it doesnt matter which guard works which shift)
shift["3_guards"]["1_hour_1_hour_22_hours"] = ((15 * 1) + (15 * 1) + (20*22)
#fill in for every combination of hours 3 guards could have
shift("4_guards")["1_hour_1_hour_1_hour_20_hours"] = ((15 * 1) + (15 * 1) (15 * 1) + (20*22)
#fill in for every combination of hours 3 guards could have
def find_lowest_cost_node(costs):
lowest_cost = float("inf")
lowest_cost_node = None
# Go through each node.
for node in costs:
cost = costs[node]
# If it's the lowest cost so far and hasn't been processed yet...
if cost < lowest_cost and node not in processed:
# ... set it as the new lowest-cost node.
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
start_time = time.perf_counter()
# Find the lowest-cost node that you haven't processed yet.
node = find_lowest_cost_node(costs)
# If you've processed all the nodes,this while loop is done.
while node is not None:
cost = costs[node]
# Go through all the neighbors of this node.
neighbors = shift[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
# If it's cheaper to get to this neighbor by going through this node...
if costs[n] > new_cost:
# ... update the cost for this node.
costs[n] = new_cost
# This node becomes the new parent for this neighbor.
parents[n] = node
# Mark the node as processed.
processed.append(node)
# Find the next node to process,and loop.
node = find_lowest_cost_node(costs)
print("Cost from the start to each node:")
print(costs)
print(time.perf_counter() - start_time,"seconds")
dijkstras_time = time.perf_counter() - start_time
# the costs table
infinity = float("inf")
costs = {}
costs["1_guard"] =
costs["2_guards"] =
costs["3_guards"] =
costs["4_guards"] = infinity
# the parents table
#need help here
parents = {}
parents[""] = "NYC"
parents[""] = "NYC"
parents[""] = "NYC"
#all the ones below a certain threshold should have None*
parents[""] = None
processed = []
我要这样做正确吗?反馈我的代码会很有帮助。
解决方法
我认为Dijkstra在这里不一定是最有用的,因为它产生了从A到B的成本最低的路径。然后,如果您真的想使用它,则可以让中间节点表示状态,即node在计划了08:00至8AM(当前警卫在午夜开始)之后,G表示状态(08:00,00:00)
。这样,最佳路径将是
(00:00,00:00) -> (01:00,00:00) -> ... -> (08:00,00:00) -> (09:00,08:00) -> ...
您实际上正在做的是动态编程,您可以在其中将问题分解为较小的子问题,并将其求解为最优。
现在,我在上面提到的新状态下使用了一些代码。这将搜索空间减少到仅325个节点。实际上,您的状况很好,只是关于我没有得到(也不需要)的父母的部分。相反,我使用带有元组键的字典,并让该值表示成本。
infinity = float("inf")
nodes = {
(current_hour,last_guard_started_hour): infinity
for current_hour in range(25)
for last_guard_started_hour in range(25)
if last_guard_started_hour <= current_hour
}
start = current = (0,0)
nodes[start] = 0
processed = []
然后,如果您像正方形(x,y)栅格那样看它,则除非您在边界处,否则邻居就是(x-1,y),(x+1,(x,y-1),y + 1)
处的节点。
def find_neighbours(current_node):
current_hour,last_guard_started_hour = current_node
# Neighbours are when either the guard started earlier/later or the current
# hour progresses/regresses
neighbours = []
if current_hour > 0 and last_guard_started_hour != current_hour:
neighbours.append((current_hour - 1,last_guard_started_hour))
if current_hour < 24:
neighbours.append((current_hour + 1,last_guard_started_hour))
if last_guard_started_hour > 0:
neighbours.append((current_hour,last_guard_started_hour - 1))
if last_guard_started_hour < current_hour:
neighbours.append((current_hour,last_guard_started_hour + 1))
return neighbours
然后您可以逐渐知道已知的解决方案直到逐渐更新节点,从而使当前保护措施处于最佳状态,因此无需重新计算。
def get_cheapest_for_hour(hour):
candidates = { n: nodes[n] for n in nodes
if n[0] == hour }
# Get the entry with the lowest cost,but also prefer longer shifts
node = min(candidates,key=lambda k: (nodes[k],k[1]))
return node
def set_cost(node):
hour,last_guard_started_hour = node
if hour == 0 and last_guard_started_hour == 0:
return
cost = 0
current_guard_hours_worked = hour - last_guard_started_hour
# If the current guard starts just now,we get the cheapest solution for the
# previous hour and add wages to that
if current_guard_hours_worked == 0:
nodes[node] = nodes[get_cheapest_for_hour(hour - 1)] + 15
return
if current_guard_hours_worked > 8:
cost += current_guard_hours_worked * 20
else:
cost += current_guard_hours_worked * 15
cost += nodes[(hour - current_guard_hours_worked,hour - current_guard_hours_worked)]
nodes[node] = cost
现在我保留了该功能,我认为它非常聪明!
def find_lowest_cost_node():
lowest_cost = infinity
lowest_cost_node = None
# Go through each node.
for node in nodes:
cost = nodes[node]
# If it's the lowest cost so far and hasn't been processed yet...
if cost < lowest_cost and node not in processed:
# ... set it as the new lowest-cost node.
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
在这里,我简化了无需父母的代码
while current is not None:
cost = nodes[current]
# Go through all the neighbours of the current node
for n in find_neighbours(current):
# Update their costs
set_cost(n)
processed.append(current)
current = find_lowest_cost_node()
print("Cost from the start to each node:")
print(nodes)
print(f"Optimal schedule cost: {nodes[(24,24)]}")
print("The optimal schedule is:")
node = (24,24)
while True:
hour,last_guard_started_hour = node
current_guard_hours_worked = hour - last_guard_started_hour
node = get_cheapest_for_hour(hour - current_guard_hours_worked)
if node[0] == 0:
break
print(f"From {node[1]}:00 to {node[0]}:00")
现在,我还没有包括最多6个警卫,但是显然可以通过将元组扩展为(current_hour,last_guard_started_hour,number_of_guards_used)
来做到这一点。祝你好运!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。