beat365亚洲投注-365提款注单审核-比分365网页版

用A*算法解决翻箱问题

用A*算法解决翻箱问题

看到问答区有个问题:

集装箱码头在堆场任务过程中(装船或提箱)存在翻箱任务,原因是由于要求的发箱顺序和码头堆放的次序不符合,如图所示,其中数字代表发箱顺序(数字越小越早发箱,可以有重复),S代表堆放的排数量(<=6),T代表堆放的高度上限(考虑堆场机任务业能力等因素,<=5),最小预翻箱问题就是,通过一系列的尽可能少的翻箱动作(如(2,2)→(4,4),(3,1)→(3,2)),把堆场倍位初始状态数字小的箱子都翻上来,避免实际任务(装船或提箱)时由于翻箱造成的任务效率损失。设计和实现最小预翻箱算法,完成如下问题:

1.基于启发式规则的A星算法用python实现(2),并输出和验证求解结果(翻箱步骤)(初始发箱顺序可随机生成),翻箱过程中不违背堆放的高度上限。

我试着解决了一下。A*算法本身没太多可说的,网上已经有很多的文章介绍。关键是确定合适的启发函数,这里我用了:

def _h(self):

length = len(self.stack.get_incorrect_containers())

adjustment = self.value / (self.stack.total_row * self.stack.max_height)

return length + 1 - adjustment

意思是如果某个翻箱动作能使错误摆放的箱子数最少,那么优先考虑该翻箱动作。如果所有可能的翻箱动作的错误箱子数相同,那么优先移动顺序最靠后的一个。

特别的,按照程序执行的结果,问题中的例子其实可以5步解决,不需要6步。

完整代码如下:

import operator

S = 4

T = 4

# 每一排箱子的发箱顺序

origin_stack = [[9, 1, 5, 0], [8, 0, 0, 0], [2, 11, 4, 0], [10, 7, 3, 6]]

# 代表堆场的类

class Stack(object):

def __init__(self, values, total_row, max_height):

# 所有箱子

self.containers = values

# 一共多少排

self.total_row = total_row

# 最大高度

self.max_height = max_height

# 当前各排实际高度

self.height = [max_height] * total_row

for i in range(total_row):

self.height[i] = -1

for j in range(max_height):

if values[i][j] != 0:

self.height[i] = j

# 取得某一排最上面一个箱子

def get_top_container(self, row):

if self.height[row] != -1:

return self.containers[row][self.height[row]]

else:

return None

# 修改某个位置箱子的发箱顺序

def set_container(self, position, value):

self.containers[position[0]][position[1]] = value

# 取得指定位置的箱子

def get_container(self, position):

return self.containers[position[0]][position[1]]

# 列举当前有哪些可移动步骤:只有最顶上的箱子可以被移动到其它有空位的排的顶上

def list_available_steps(self, close_steps):

available_steps = []

for i in range(self.total_row):

if self.height[i] < 0:

continue

for j in range(self.total_row):

if i != j and self.height[j] < self.max_height - 1:

cloned_stack = self.__copy__()

top_container = cloned_stack.get_top_container(i)

new_step = Step(top_container, (i, self.height[i]), (j, self.height[j] + 1), cloned_stack)

if new_step not in close_steps:

available_steps.append(new_step)

return available_steps

# 取得所有摆放位置不对的箱子

def get_incorrect_containers(self, row=-1):

if row == -1:

return self._get_incorrect_containers(range(self.total_row))

else:

return self._get_incorrect_containers(range(row, row + 1))

# 取得所有摆放位置不对的箱子:如果发箱顺序小的箱子位于发箱顺序大的箱子之下,那么认为这个发箱顺序小的箱子向上的所有箱子位置都不对

def _get_incorrect_containers(self, rows):

result = []

for i in rows:

for j in range(1, self.max_height):

if self.containers[i][j] > self.containers[i][j - 1]:

for k in range(j, self.max_height):

if self.containers[i][k] != 0:

result.append(self.containers[i][k])

break

return result

# 把箱子从move_from位置移动到move_to位置

def move(self, move_from, move_to):

self.set_container(move_to, self.get_container(move_from))

self.set_container(move_from, 0)

self.height[move_from[0]] -= 1

self.height[move_to[0]] += 1

# 打印当前堆场

def print(self):

for j in range(self.max_height):

for i in range(self.total_row):

print(str(self.containers[i][-j - 1]).rjust(4), end='\t')

print()

# 判断两个堆场是否相等:比较所有位置的箱子,如果所有箱子的发箱顺序一样,则相等

def __eq__(self, other):

return operator.eq(self.containers, other.containers)

# 深度复制堆场

def __copy__(self):

cloned_containers = [[None for i in range(self.max_height)] for j in range(self.total_row)]

for i in range(self.total_row):

for j in range(self.max_height):

cloned_containers[i][j] = self.containers[i][j]

return Stack(cloned_containers, self.total_row, self.max_height)

# 代表移动步骤的类

class Step(object):

def __init__(self, container, move_from, move_to, stack):

# 被移动的箱子

self.value = container

# 箱子移动前的位置

self.move_from = move_from

# 箱子移动后的位置

self.move_to = move_to

# 堆场

self.stack = stack

# 前一个步骤

self.parent = None

# 当前是第几步

self.order = 0

# 修改堆场状态,实际移动箱子

self.stack.move(self.move_from, self.move_to)

# 修改前一个步骤

def set_parent(self, parent):

self.parent = parent

self.order = parent.get_order() + 1

# 获得前一个步骤

def get_parent(self):

return self.parent

# 获得当前是第几步

def get_order(self):

return self.order

# 获得当前步骤执行后对应的堆场状态

def get_stack(self):

return self.stack

# 启发函数

def _h(self):

length = len(self.stack.get_incorrect_containers())

adjustment = self.value / (self.stack.total_row * self.stack.max_height)

return length - adjustment

# 计算从开始状态到当前实际多少步

def _g(self):

return self.order

# 估值函数

def f(self):

return self._g() + self._h()

# 判断两个步骤是否等价:如果对应的堆场状态完全一样,则两个步骤等价

def __eq__(self, other):

return operator.eq(self.stack, other.get_stack())

# tostring的输出格式

def __str__(self):

return f'({self.value}: {self.move_from} -> {self.move_to}, {self.order}, {self.f()})'

# 打印时的表现形式

def __repr__(self):

return self.__str__()

if __name__ == '__main__':

# 最初的堆场状态

origin = Stack(origin_stack, S, T)

origin.print()

open_steps = []

close_steps = []

# 将最初堆场的可用步骤加入open list

open_steps.extend(origin.list_available_steps(close_steps))

# 根据估值函数最小的原则找到移动的第一步

current_step = min(open_steps, key=lambda x: x.f())

# 第一步移动后的堆场状态

current_stack = current_step.get_stack()

# 如果移动后还是有摆放位置不对的箱子,则继续调整

while len(current_stack.get_incorrect_containers()) > 0:

available_steps = current_stack.list_available_steps(close_steps)

# A*算法的处理:注意步骤比较会按照Step类的__eq__方法,即比较步骤对应的堆场状态

for step in available_steps:

# 如果步骤在open list中,即open list中有其它步骤形成了与可用步骤相同的堆场状态

if step in open_steps:

open_step = open_steps[open_steps.index(step)]

# 与open list中等价步骤的order进行比较,如果原有步骤的order比可用步骤大,则删除原有的步骤, 否则跳过可用步骤

if open_step.get_order() > step.get_order():

open_steps.remove(open_step)

else:

continue

# 将可用步骤链接到当前步骤之后,然后加入open list

step.set_parent(current_step)

open_steps.append(step)

# 当前步骤被从open list移动到close list

open_steps.remove(current_step)

close_steps.append(current_step)

# 如果已经没有可用的步骤了,输出无法完成,结束程序

if len(open_steps) == 0:

print("It can't be done.")

exit(0)

# 根据估值函数最小的原则找到移动的下一步

current_step = min(open_steps, key=lambda x: x.f())

current_stack = current_step.get_stack()

# 如果正确退出循环,说明找到了移动方法,倒序打印所有步骤

actual_steps = [current_step]

while current_step.get_parent() is not None:

actual_steps.append(current_step.get_parent())

current_step = current_step.get_parent()

for step in reversed(actual_steps):

print(step)

step.get_stack().print()

# 打印步骤数

print(f'It could be done in {len(actual_steps)} steps.')

相关推荐