Python heapq如何解析相等值?
来自Java,我试图用python实现,但在对图中具有相等f分数的顶点进行排序时遇到了问题。我正试图使用heapq来实现这一点,经过一些调试后,我注意到如果我想要推送一个顶点,该顶点的f值等于堆中其他一些预先存在的顶点,那么顺序就会混乱。我现在正在考虑实现自己的优先级队列。我想知道这是怎么回事 这种行为的一个例子如下:Python heapq如何解析相等值?,python,priority-queue,heapq,Python,Priority Queue,Heapq,来自Java,我试图用python实现,但在对图中具有相等f分数的顶点进行排序时遇到了问题。我正试图使用heapq来实现这一点,经过一些调试后,我注意到如果我想要推送一个顶点,该顶点的f值等于堆中其他一些预先存在的顶点,那么顺序就会混乱。我现在正在考虑实现自己的优先级队列。我想知道这是怎么回事 这种行为的一个例子如下: >>> mylist = [1, 2, 5, 4, 3] >>> heapq.heapify(mylist) >>> myl
>>> mylist = [1, 2, 5, 4, 3]
>>> heapq.heapify(mylist)
>>> mylist
>>> [1, 2, 3, 4, 5]
>>> heapq.heappush(mylist, 1)
>>> mylist
>>> [1, 2, 1, 4, 5, 3]
下面是我为上下文实现的实际代码:
class Node(object):
def __init__(self, name, x_coordinate, y_coordinate, obstacle_flag=False):
self.name = name # possible values should only be ' ', 'A-Z', '*'
self.coordinates = (x_coordinate, y_coordinate) # this will uniquely identify the node
self.obstacle = obstacle_flag # if the name is '*' the obstacle is set to True
self.neighbors = {} # list of neighbors of this node
self.set_obstacle()
...
class Vertex(Node):
def __init__(self, name, x_coordinate, y_coordinate, obstacle_flag):
super(Vertex, self).__init__(name, x_coordinate, y_coordinate, obstacle_flag)
self.g_actual_cost = 10000
self.h_cost = 0 # the cost given by the heuristic function
self.previous_vertex = None
self.total_cost = self.g_actual_cost + self.h_cost
def __lt__(self, other):
return self.total_cost < other.total_cost
def __eq__(self, other):
if isinstance(other, Vertex):
return self.total_cost == other.total_cost
return NotImplemented
秩序没有混乱。对于所有索引,堆应具有:
a[i] <= a[2*i + 1]
a[i] <= a[2*i + 2]
秩序没有混乱。对于所有索引,堆应具有:
a[i] <= a[2*i + 1]
a[i] <= a[2*i + 2]
Heap并不意味着排序,如果排序了,就不能及时为任意值构建它。这意味着它满足堆不变量,对于像Python这样的最小堆,这只意味着如果出现平局,最小值位于顶部,任意值获胜,并且每个节点的子节点总是等于或大于该节点。通过查看索引2*i+1和2*i+2,您可以找到索引i处节点的子节点,因此在您的示例中,将P放在每个父节点下,将C放在每个子节点下,我们得到:
[1, 2, 1, 4, 5, 3]
#P C C
#0 1 2
[1, 2, 1, 4, 5, 3]
# P C C
# 1 3 4
[1, 2, 1, 4, 5, 3]
# P C
# 2 5 (only one child since heap lacks another element)
如您所见,在每种情况下,p值都小于或等于其所有子项;保持堆不变量,这是heappop、heappush等继续工作所必需的
请注意,对于像Vertex类这样的对象,比较基于一个值,但对象具有其他状态,堆是不稳定的;具有相同总成本的两个对象可以按任意顺序出现,而不管哪个对象先放在堆中。为了避免这个问题,您必须通过修饰每个值来添加您自己的回退比较。一个简单的方法是:
from itertools import count
original_list = [Vertex(...), Vertex(...), ...] # Define initial list of vertices
numbermaker = count()
decorated_list = [(v, next(numbermaker)) for v in original_list]
heapq.heapify(decorated_list)
# When you want to add new elements:
heapq.heappush(decorated_list, (Vertex(...), next(numbermaker)))
# When you want to get the top of the heap's value:
top = decorated_list[0][0] # First [0] gets decorated top, second strips decoration
# When you want to pop off the top:
top = heapq.heappop(decorated_list)[0]
Heap并不意味着排序,如果排序了,就不能及时为任意值构建它。这意味着它满足堆不变量,对于像Python这样的最小堆,这只意味着如果出现平局,最小值位于顶部,任意值获胜,并且每个节点的子节点总是等于或大于该节点。通过查看索引2*i+1和2*i+2,您可以找到索引i处节点的子节点,因此在您的示例中,将P放在每个父节点下,将C放在每个子节点下,我们得到:
[1, 2, 1, 4, 5, 3]
#P C C
#0 1 2
[1, 2, 1, 4, 5, 3]
# P C C
# 1 3 4
[1, 2, 1, 4, 5, 3]
# P C
# 2 5 (only one child since heap lacks another element)
如您所见,在每种情况下,p值都小于或等于其所有子项;保持堆不变量,这是heappop、heappush等继续工作所必需的
请注意,对于像Vertex类这样的对象,比较基于一个值,但对象具有其他状态,堆是不稳定的;具有相同总成本的两个对象可以按任意顺序出现,而不管哪个对象先放在堆中。为了避免这个问题,您必须通过修饰每个值来添加您自己的回退比较。一个简单的方法是:
from itertools import count
original_list = [Vertex(...), Vertex(...), ...] # Define initial list of vertices
numbermaker = count()
decorated_list = [(v, next(numbermaker)) for v in original_list]
heapq.heapify(decorated_list)
# When you want to add new elements:
heapq.heappush(decorated_list, (Vertex(...), next(numbermaker)))
# When you want to get the top of the heap's value:
top = decorated_list[0][0] # First [0] gets decorated top, second strips decoration
# When you want to pop off the top:
top = heapq.heappop(decorated_list)[0]
堆不是排序列表。列表未排序这一事实不是问题。堆不是排序列表。列表没有排序这一事实不是问题。我明白了。使用自定义对象时是否仍然满足不变量?我使用的是total_score作为优先级。@Timbwa:如果有两个对象具有相同的total_成本,堆会半任意地对它们排序。一次堆出一个对象并不是一种稳定的排序,但是的,不变量仍然满足要求。如果需要稳定性,则必须使用单调递增的值来修饰它们,例如,创建一个count对象,numbermaker=itertools.count,而不是在堆中使用原始值,使用元组将原始值与nextnumbermaker配对,以便每个新值后面都有一个更大的断开连接的值。^enumerate也可用作tiebreaker@wim:是的。计数的优点是,您可以将对象放在一边,并在以后添加新元素时重用它;enumerate只适用于处理单个输入iterable,然后您就失去了添加更多不相关元素的状态,而在heapify之后OP显然会推动更多的元素。如果你只需要对一个iterable进行heapify,然后再也不推/替换pop,enumerate很好,遗憾的是,你必须对它返回的内容重新排序,因为它首先给出索引,然后给出值。我明白了。使用自定义对象时是否仍然满足不变量?我使用的是total_score作为优先级。@Timbwa:如果有两个对象具有相同的total_成本,堆会半任意地对它们排序。一次堆出一个对象并不是一种稳定的排序,但是的,不变量仍然满足要求。如果需要稳定性,则必须使用单调递增的值来修饰它们,例如,创建一个count对象,numbermaker=itertools.count,而不是在堆中使用原始值,使用元组将原始值与nextnumbermaker配对,以便每个新值后面都有一个更大的断开连接的值。^enumerate也可用作tiebreaker@wim:是的。计数的优点是,您可以将对象放在一边并重用它 以后添加新元素时;enumerate只适用于处理单个输入iterable,然后您就失去了添加更多不相关元素的状态,而在heapify之后OP显然会推动更多的元素。如果您只需要heapify一个iterable,然后再也不推送/替换pop,enumerate很好,遗憾的是,您必须对它返回的内容重新排序,因为它首先给出索引,然后给出值。