diff --git a/node.py b/node.py index 14dce07ca7bf368c6af514ed46df043611ea2912..36e56169f87a2ad49a8e8934fa7fd99f905ee679 100755 --- a/node.py +++ b/node.py @@ -1,16 +1,19 @@ #!/usr/bin/python3 +"""Base object for construction of all heap variants""" + + class Node: def __init__(self, key): - #for now contains all pointers that might be needed in any implementation - #idea is to use only necessary ones in each implementation + """contains all pointers that might be needed in any implementation. + Only necessary ones used in each implementation""" self.key = key self.parent = None self.leftChild = None - self.rightChild=None - self.nextSibling=None - self.prevSibling=None - self.leftOnly=False - self.rightOnly=False + self.rightChild = None + self.nextSibling = None + self.prevSibling = None + self.leftOnly = False + self.rightOnly = False - self.vertex=None #for testing with Dijkstra's algorithm + self.vertex = None # used for testing with Dijkstra's algorithm diff --git a/pairing_heap.py b/pairing_heap.py index 8ff4c91a40a9d6e2aebc41e9439e66abea90a178..be23a3b76edaa6f251fd9d95a97291610eeaf831 100644 --- a/pairing_heap.py +++ b/pairing_heap.py @@ -1,4 +1,7 @@ #!/usr/bin/python3 +"""'Universal pairing heap'; +behaviour is decided upon initialisation by a type ID""" + from node import Node from pairing_heap_interface import PairingHeapInterface from pairing_heap_standard import PairingHeapStandard @@ -10,69 +13,72 @@ COUNT_TYPE_LINKS=-1 COUNT_TYPE_COMPS=-2 COUNT_TYPE_BOTH=0 + class PairingHeap(PairingHeapInterface): - MODES={21:"Pairing_L", 22:"Smooth_L"} - mode=0 - countType=COUNT_TYPE_COMPS - heap=None + MODES = {21: "Pairing_L", 22: "Smooth_L"} + mode = 0 + countType = COUNT_TYPE_COMPS + heap = None + def __init__(self, mode=0, countType=COUNT_TYPE_COMPS): self.mode=mode self.countType=countType def make_heap(self): - if self.mode==0: - self.heap=PairingHeapStandard() - elif self.mode==12: - self.heap=SmoothHeap() - elif self.mode==21:#root list version, everything lazy, to be used for Dijkstra test in paper - self.heap=PairingHeapL() - elif self.mode==22:#root list version, everything lazy, to be used for Dijkstra test in paper - self.heap=SmoothHeapL() + if self.mode == 0: + self.heap = PairingHeapStandard() + elif self.mode == 12: + self.heap = SmoothHeap() + elif self.mode == 21: # root list version, everything lazy, to be used for Dijkstra test in paper + self.heap = PairingHeapL() + elif self.mode == 22: # root list version, everything lazy, to be used for Dijkstra test in paper + self.heap = SmoothHeapL() else: raise Exception("Invalid heap ID! No heap of type ID {} is implemented.") def find_min(self): - #Careful! Implementation is inconsistent across pairing heap types. return self.heap.find_min() def insert(self, node): - #inserts node; returns number of linking operations performed - result=self.heap.insert(node) + """inserts node; returns number of comparisons and + number of linking operations performed""" + result = self.heap.insert(node) if isinstance(result, tuple): - if self.countType==COUNT_TYPE_BOTH: - return (result[0], result[1])#(comps, links) + if self.countType == COUNT_TYPE_BOTH: + return (result[0], result[1]) # (comps, links) else: return result[2+self.countType] - else:#result should be single number iff link count and comp count are the same - if self.countType==COUNT_TYPE_BOTH: + else: # result should be single number iff link count and comp count are the same + if self.countType == COUNT_TYPE_BOTH: return (result, result) else: return result def delete_min(self): - #deletes min; returns number of linking operations/comparisons performed - result=self.heap.delete_min() - if len(result)==3: - if self.countType==COUNT_TYPE_BOTH: - return (result[0], result[1], result[2])#min, comps, links + """deletes min; returns number of linking operations/comparisons performed""" + result = self.heap.delete_min() + if len(result) == 3: + if self.countType == COUNT_TYPE_BOTH: + return (result[0], result[1], result[2]) # min node, comps, links else: - return (result[0],result[3+self.countType]) - else:#result should be 2-tuple iff link count and comp count are the same - if self.countType==COUNT_TYPE_BOTH: + return (result[0], result[3+self.countType]) + else: # result should be 2-tuple iff link count and comp count are the same + if self.countType == COUNT_TYPE_BOTH: return (result[0], result[1], result[1]) else: return result def merge(self, heap2): - #merges this heap and heap 2; returns number of linking operations performed + """merges this heap and heap 2; + returns number of comparisons and linking operations performed""" result = self.heap.merge(heap2) if isinstance(result, tuple): - if self.countType==COUNT_TYPE_BOTH: - return (result[0], result[1])#(comps, links) + if self.countType == COUNT_TYPE_BOTH: + return (result[0], result[1]) # (comps, links) else: return result[2+self.countType] - else:#result should be single number iff link count and comp count are the same - if self.countType==COUNT_TYPE_BOTH: + else: # should be single number iff link count and comp count are the same + if self.countType == COUNT_TYPE_BOTH: return (result, result) else: return result @@ -81,18 +87,20 @@ class PairingHeap(PairingHeapInterface): self.heap.delete(node) def decrease_key(self, node, diff): + """performs decrease-key; + returns number of comparisons and linking operations performed""" result = self.heap.decrease_key(node, diff) if isinstance(result, tuple): - if self.countType==COUNT_TYPE_BOTH: - if result[0]==None or result[1]==None: - print("heap {} returns None".format(self.MODES[self.mode])) + if self.countType == COUNT_TYPE_BOTH: + if result[0] is None or result[1] is None: + raise Exception("heap {} returns None".format(self.MODES[self.mode])) return (result[0], result[1])#(comps, links) else: return result[2+self.countType] - else:#result should be single number iff link count and comp count are the same - if self.countType==COUNT_TYPE_BOTH: - if result==None: - print("heap {} returns None".format(self.MODES[self.mode])) + else: # result should be single number iff link count and comp count are the same + if self.countType == COUNT_TYPE_BOTH: + if result is None: + raise Exception("heap {} returns None".format(self.MODES[self.mode])) return (result, result) else: return result diff --git a/pairing_heap_interface.py b/pairing_heap_interface.py index bd14710d93ee86b345d9b9433f2b297a9c3d0706..22e43d398e3f2043b7cf9207713600f7fe436520 100755 --- a/pairing_heap_interface.py +++ b/pairing_heap_interface.py @@ -1,5 +1,6 @@ #!/usr/bin/python3 -from node import Node +"""interface implemented by all heap variants""" + class PairingHeapInterface: def __init__(self): self.count = 0 diff --git a/pairing_heap_l.py b/pairing_heap_l.py index bef91433fbc70ee5c1ab1a4b2fcad9c4ac4bdc76..8cdf4cf81715f47694b011cd8c5e51735ea6f4f2 100644 --- a/pairing_heap_l.py +++ b/pairing_heap_l.py @@ -2,180 +2,175 @@ from node import Node from pairing_heap_interface import PairingHeapInterface + class PairingHeapL(PairingHeapInterface): - # lazy variant of standard pairing heap (maintaining root-list and consolidating only upon extract-min) - forest=[] #list storing roots of all top-level trees - def __init__(self, root=None): - self.forest=[] - if root!=None: - root.parent=None - self.forest+=[root] + """lazy variant of standard pairing heap + (maintaining root-list and consolidating only upon extract-min)""" + forest = [] # list storing roots of all top-level trees + + def __init__(self, root=None): + self.forest = [] + if root is not None: + root.parent = None + self.forest += [root] + + def listInorder(self): + forestList = [] + for root in self.forest: + forestList += [self.listInorderTree(root)] + return forestList - def listInorder(self): - forestList=[] - for root in self.forest: - forestList+=[self.listInorderTree(root)] - return forestList + def listInorderTree(self, root): + if root is None: + return [] + else: + return self.listInorderTree(root.leftChild) + [root.key] + self.listInorderTree(root.nextSibling) - def listInorderTree(self, root): - if root==None: - return [] - else: - return self.listInorderTree(root.leftChild)+[root.key]+self.listInorderTree(root.nextSibling) + def insert(self, node): + # concatenates node to list of trees; returns number of linking ops (always 0) for sake of consistency + # print("trying to insert {}...".format(node.key)) + if node is None: + return 0 + node.parent = None + self.forest += [node] + return 0 - def insert(self, node): - #concatenates node to list of trees; returns number of linking ops (always 0) for sake of consistency - #print("trying to insert {}...".format(node.key)) - if node==None: - return 0 - node.parent=None - self.forest+=[node] - return 0 + def pairing(self): + """performs consolidation (left-to-right pairing pass, linking pairs of neighbours, + followed by right-to-left combining) and returns number of linking operations""" + fs = len(self.forest) + if fs < 2: + return 0 + else: + # right-to-left-pairing pass + pairedForest = [] + for i in range(0, fs, 2): + if i == fs - 1: # last tree if length of forest is odd-numbered + pairedForest += [self.forest[i]] # concatenate to new forest (no linking required) + else: # link neighbouring roots + if self.forest[i].key <= self.forest[i + 1].key: + if self.forest[i].leftChild == None: + self.forest[i + 1].parent = self.forest[i] + else: + self.forest[i + 1].nextSibling = self.forest[i].leftChild + self.forest[i].leftChild = self.forest[i + 1] + pairedForest += [self.forest[i]] + else: + if self.forest[i + 1].leftChild == None: + self.forest[i].parent = self.forest[i + 1] + else: + self.forest[i].nextSibling = self.forest[i + 1].leftChild + self.forest[i + 1].leftChild = self.forest[i] + pairedForest += [self.forest[i + 1]] + self.forest = pairedForest - def pairing(self): - fs = len(self.forest) - #performs pairing pass and returns number of linking operations - linkCount=0 - if fs<2: - return 0 - else: - pairedForest=[] - index=-1 - for i in range(0, fs, 2): # pairing pass - if i==fs-1: #last tree if length of forest is odd-numbered - pairedForest+=[self.forest[i]]#concatenate to new forest (no linking required) - else:#pair trees - if self.forest[i].key<=self.forest[i+1].key: - if self.forest[i].leftChild==None: - self.forest[i+1].parent=self.forest[i] - else: - self.forest[i+1].nextSibling=self.forest[i].leftChild - self.forest[i].leftChild=self.forest[i+1] - pairedForest+=[self.forest[i]] - else: - if self.forest[i+1].leftChild==None: - self.forest[i].parent=self.forest[i+1] - else: - self.forest[i].nextSibling=self.forest[i+1].leftChild - self.forest[i+1].leftChild=self.forest[i] - pairedForest+=[self.forest[i+1]] - self.forest=pairedForest - ##print("Result of 1st round {}.".format(self.listInorder())) - index = len(self.forest)-1 - for i in range(len(self.forest)-2, -1, -1): # combining pass - if self.forest[index].key<=self.forest[i].key: - if self.forest[index].leftChild==None: - self.forest[i].parent=self.forest[index] - else: - self.forest[i].nextSibling=self.forest[index].leftChild - self.forest[index].leftChild=self.forest[i] - else: - if self.forest[i].leftChild==None: - self.forest[index].parent=self.forest[i] - else: - self.forest[index].nextSibling=self.forest[i].leftChild - self.forest[i].leftChild=self.forest[index] - index=i + # right-to-left combining pass + index = len(self.forest) - 1 + for i in range(len(self.forest) - 2, -1, -1): # link two rightmost roots + if self.forest[index].key <= self.forest[i].key: + if self.forest[index].leftChild == None: + self.forest[i].parent = self.forest[index] + else: + self.forest[i].nextSibling = self.forest[index].leftChild + self.forest[index].leftChild = self.forest[i] + else: + if self.forest[i].leftChild == None: + self.forest[index].parent = self.forest[i] + else: + self.forest[index].nextSibling = self.forest[i].leftChild + self.forest[i].leftChild = self.forest[index] + index = i - self.forest=[self.forest[index]] - ##print("Result of 2nd round {}.".format(self.listInorder())) - return (fs-1) + self.forest = [self.forest[index]] + return (fs - 1) # number of links needed to consolidate n roots is n-1 - def delete_min(self): - #finds and deletes min; restructures forest; returns number of linking operations - linkCount=0 - compCount=0 - cn = self.pairing() - assert len(self.forest)==1 - #if (len(self.forest)==0): - # print("Cannot delete min of empty heap") - # return (None,0,0) - currentSibling=self.forest[0].leftChild - while currentSibling!=None: - nextSibling=currentSibling.nextSibling - self.forest+=[currentSibling] - currentSibling.nextSibling=None - currentSibling=nextSibling - self.forest[-1].parent=None #only for the last concatenated sibling as only this one carried parent pointer - minNode = self.forest[0] - self.forest=self.forest[1:] - ##print("Result of delete-min {}.".format(self.listInorder())) - ###print('**') - ###print(minNode.key) - return (minNode, cn, cn) + def delete_min(self): + """finds and deletes min; restructures forest; returns number of linking operations""" + cn = self.pairing() + assert len(self.forest) == 1 + currentSibling = self.forest[0].leftChild + while currentSibling != None: + nextSibling = currentSibling.nextSibling + self.forest += [currentSibling] + currentSibling.nextSibling = None + currentSibling = nextSibling + self.forest[-1].parent = None # only for the last concatenated sibling as only this one carried parent pointer + minNode = self.forest[0] + self.forest = self.forest[1:] + return (minNode, cn, cn) - def decrease_key(self, node, diff): - linkCount=0 - if node==None or diff<=0: - return 0 - elif node.parent==None and node.nextSibling==None: #node is root - node.key=node.key-diff - else: - self.unlink_node(node) - node.key=node.key-diff - self.forest+=[node] - return 0 + def decrease_key(self, node, diff): + """unlinks node from current position in tree (if inner node), + decreases key, adds node with subtree to root list""" + if node is None or diff <= 0: + return 0 + elif node.parent is None and node.nextSibling is None: # node is root + node.key = node.key - diff + else: + self.unlink_node(node) + node.key = node.key - diff + self.forest += [node] + return 0 - def merge(self, heap2): - #concatenates forests of this heap and heap2; returns number of link operations (always 0) for consistency - #print("Trying to merge {} and {}.".format(self.listInorder(), heap2.listInorder())) - self.forest+=heap2.forest - #print("Result of merge is {}.".format(self.listInorder())) - return 0 + def merge(self, heap2): + """concatenates forests of this heap and heap2; returns number of link operations (always 0) for consistency""" + self.forest += heap2.forest + return 0 - def delete(self, node): - if node==None: - print("Cannot delete None") - return - elif node.parent==None and node.nextSibling==None: #node is root - print("Trying to delete {}...".format(node.key)) - index=self.forest.index(node)#slight cheating; would be nicer to use a linked list as forest instead - #remove node from forest list - self.forest=self.forest[:index]+self.forest[index+1:] - else: #node is a child somewhere - print("Trying to delete {}...".format(node.key)) - self.unlink_node(node) - #concatenate potential children to forest list - sibling=node.leftChild - while sibling!=None: - self.forest+=[sibling] - sibling=sibling.nextSibling - if sibling!=None: - self.forest[-1].nextSibling=None - else: - self.forest[-1].parent=None - print("Result of deletion of {} is {}.".format(node.key, self.listInorder())) + def delete(self, node): + """deletes node from heap; concatenates orphaned children to list of roots""" + if node is None: + # print("Cannot delete None") + return + elif node.parent is None and node.nextSibling is None: # node is root + # print("Trying to delete {}...".format(node.key)) + index = self.forest.index(node) # slight cheating; would be nicer to use a linked list as forest instead + # remove node from forest list + self.forest = self.forest[:index] + self.forest[index + 1:] + else: # node is a child somewhere + # print("Trying to delete {}...".format(node.key)) + self.unlink_node(node) + # concatenate potential children to forest list + sibling = node.leftChild + while sibling is not None: + self.forest += [sibling] + sibling = sibling.nextSibling + if sibling is not None: + self.forest[-1].nextSibling = None + else: + self.forest[-1].parent = None + print("Result of deletion of {} is {}.".format(node.key, self.listInorder())) - - def unlink_node(self, node): - #for non-root nodes only (does nothing about forest list, only tree-internal links) - if node==None: - return - else: - if node.nextSibling!=None: - temp=node.nextSibling - while temp.nextSibling!=None:#find rightmost child - temp=temp.nextSibling - if temp.parent.leftChild==node:#node is leftmost child - #link parent to next sibling - temp.parent.leftChild=node.nextSibling - node.nextSibling=None - else: - #node is neither first nor last child of parent - prevSibling=temp.parent.leftChild - while prevSibling.nextSibling!=node:#find left (previous) sibling - prevSibling=prevSibling.nextSibling - prevSibling.nextSibling=node.nextSibling #cut out node, link left and right sibling - node.nextSibling=None - else: - #node is rightmost child of parent - if node.parent.leftChild==node: - #node is only child: just remove - node.parent.leftChild=None - else: - prevSibling=node.parent.leftChild - while prevSibling.nextSibling!=node:#find left (previous) sibling - prevSibling=prevSibling.nextSibling - prevSibling.parent=node.parent - prevSibling.nextSibling=None - node.parent=None + def unlink_node(self, node): + """for non-root nodes only: unlinks node from current location, re-establishes links in remaining heap + (does nothing about forest list, only tree-internal links)""" + if node == None: + return + else: + if node.nextSibling != None: + temp = node.nextSibling + while temp.nextSibling != None: # find rightmost child + temp = temp.nextSibling + if temp.parent.leftChild == node: # node is leftmost child + # link parent to next sibling + temp.parent.leftChild = node.nextSibling + node.nextSibling = None + else: + # node is neither first nor last child of parent + prevSibling = temp.parent.leftChild + while prevSibling.nextSibling != node: # find left (previous) sibling + prevSibling = prevSibling.nextSibling + prevSibling.nextSibling = node.nextSibling # cut out node, link left and right sibling + node.nextSibling = None + else: + # node is rightmost child of parent + if node.parent.leftChild == node: + # node is only child: just remove + node.parent.leftChild = None + else: + prevSibling = node.parent.leftChild + while prevSibling.nextSibling != node: # find left (previous) sibling + prevSibling = prevSibling.nextSibling + prevSibling.parent = node.parent + prevSibling.nextSibling = None + node.parent = None diff --git a/pairing_heap_standard.py b/pairing_heap_standard.py index f1abe256cb512def1a8459b979d96e02c140e52d..bfd883dde0bd6c48fc0f833255c25d839dd1d182 100644 --- a/pairing_heap_standard.py +++ b/pairing_heap_standard.py @@ -2,170 +2,176 @@ from node import Node from pairing_heap_interface import PairingHeapInterface + class PairingHeapStandard(PairingHeapInterface): - #performs a left-to-right forward pass, then a backward combining pass - #TODO: left/right child (page 115) + """standard pairing heap + performs a left-to-right forward pass, then a backward combining pass to consolidate""" + def __init__(self, root=None): - self.root=root + self.root = root def make_heap(self): - #this is equivalent to init + # this is equivalent to init pass - + def listInorder(self, root): - if(root==None): + if (root is None): return [] - return self.listInorder(root.leftChild)+[root.key]+self.listInorder(root.nextSibling) + return self.listInorder(root.leftChild) + [root.key] + self.listInorder(root.nextSibling) def find_min(self): - if self.root==None: + if self.root is None: return None else: return self.root def insert(self, node): - #inserts node as child of root, returns number of link operations - linkCount=0 - #print("trying to insert {}...".format(node.key)) - if self.root==None: - #heap was empty before - self.root=node + """ inserts node by linking to current root, + returns number of link operations""" + linkCount = 0 + if self.root is None: + # heap was empty before + self.root = node else: - newheap=PairingHeapStandard(node) - linkCount=self.merge(newheap) - #print(self.listInorder(self.root)) + newheap = PairingHeapStandard(node) + linkCount = self.merge(newheap) + # print(self.listInorder(self.root)) return linkCount def delete_min(self): - #print("trying to delete min...") - linkCount=0 #counts number of linking operations - minKey=None - minNode=None - if self.root==None: + """Extracts minimum (current root), consolidates orphaned children. + returns number of link operations""" + linkCount = 0 # counts number of linking operations + minNode = None + if self.root is None: print("Heap was already empty.") - return (minNode,linkCount) - elif self.root.leftChild==None: - #heap contained only one element - minNode=self.root - self.root=None return (minNode, linkCount) - elif self.root.leftChild.nextSibling==None: - #first child has no siblings->first child becomes root - minNode=self.root - self.root=self.root.leftChild - self.root.parent=None + elif self.root.leftChild is None: + # heap contained only one element + minNode = self.root + self.root = None + return (minNode, linkCount) + elif self.root.leftChild.nextSibling is None: + # first child has no siblings->first child becomes root + minNode = self.root + self.root = self.root.leftChild + self.root.parent = None return (minNode, linkCount) else: - minNode=self.root - self.root=self.root.leftChild - current=self.root - nextSibling=None - heaps=[] - paired=[] - #left-to-right pairing pass v2 - while current!=None:#create heaps of all orphaned children - nextSibling=current.nextSibling - heaps+=[PairingHeapStandard(current)] - current.nextSibling=None - current=nextSibling - for j in range(0,len(heaps),2): - if(j==len(heaps)-1):#last one - paired+=[heaps[j]] + minNode = self.root + self.root = self.root.leftChild + current = self.root + nextSibling = None + heaps = [] + paired = [] + # left-to-right pairing pass + while current is not None: # create heaps of all orphaned children + nextSibling = current.nextSibling + heaps += [PairingHeapStandard(current)] + current.nextSibling = None + current = nextSibling + for j in range(0, len(heaps), 2): + if j == (len(heaps) - 1): # last one + paired += [heaps[j]] else: - heap=heaps[j] - linkCount+=heap.merge(heaps[j+1])#merge returns its number of link operations - paired+=[heap] - #combining backwards pass v2 - combined=paired[-1]#start with last tree - for i in range(len(paired)-2, -1, -1): - linkCount+=combined.merge(paired[i])#merge returns its number of link operations - self.root=combined.root - self.root.parent=None - #print("result is {}".format(self.listInorder(self.root))) + heap = heaps[j] + linkCount += heap.merge(heaps[j + 1]) # merge returns its number of link operations + paired += [heap] + # combining backwards (right-to-left) pass + combined = paired[-1] # start with last (rightmost) tree + for i in range(len(paired) - 2, -1, -1): + linkCount += combined.merge(paired[i]) # merge returns its number of link operations + self.root = combined.root + self.root.parent = None return (minNode, linkCount) def merge(self, heap2): - linkCount=0 #counts number of linking operations - #print("Trying to merge {} and {}...".format(self.listInorder(self.root), self.listInorder(heap2.root))) - if self.root==None:#heap is empty - self.root=heap2.root - elif heap2.root==None:#heap 2 is empty - pass #this heap is the result + """merges heap2 with current heap by linking roots. + returns number of link operations""" + linkCount = 0 # counts number of linking operations + if self.root is None: # heap is empty + self.root = heap2.root + elif heap2.root is None: # heap 2 is empty + pass # this heap is the result else: - if self.root.key<=heap2.root.key: - heap2.root.nextSibling=self.root.leftChild - if heap2.root.nextSibling==None: - heap2.root.parent=self.root - self.root.leftChild=heap2.root - linkCount=1 + # link roots + if self.root.key <= heap2.root.key: + heap2.root.nextSibling = self.root.leftChild + if heap2.root.nextSibling is None: + heap2.root.parent = self.root + self.root.leftChild = heap2.root + linkCount = 1 else: - self.root.nextSibling=heap2.root.leftChild - if self.root.nextSibling==None: - self.root.parent=heap2.root - heap2.root.leftChild=self.root - self.root=heap2.root - linkCount=1 - #TODO check for only children? - #print("Result is {}".format(self.listInorder(self.root))) + self.root.nextSibling = heap2.root.leftChild + if self.root.nextSibling is None: + self.root.parent = heap2.root + heap2.root.leftChild = self.root + self.root = heap2.root + linkCount = 1 return linkCount - def decrease_key(self, node, diff):#TODO more testing - linkCount=0 - if self.root==node: - self.root.key=self.root.key-diff + def decrease_key(self, node, diff): + """cuts node with subtree from current place in tree; + decreases key; links node to root""" + linkCount = 0 + if self.root == node: + self.root.key = self.root.key - diff else: - #first step: cut node from heap - self.unlink_node(node)#helper function - #second step: decrease key - subheap=PairingHeapStandard(node) - subheap.root.key=subheap.root.key-diff - #third step: merge back in + # first step: cut node from heap + self.unlink_node(node) # helper function + # second step: decrease key + subheap = PairingHeapStandard(node) + subheap.root.key = subheap.root.key - diff + # third step: merge back in linkCount = self.merge(subheap) return linkCount - def delete(self, node): #TODO more testing? - print("trying to delete {} from {}".format(node.key, self.listInorder(self.root))) - if self.root.key==node.key: - self.delete_min() + def delete(self, node): + """removes node with subtree from current place in tree; + deletes node, consolidating orphaned children; + links consolidated subtree to root. + returns number of link operations""" + if node is None: + return 0 + if self.root.key == node.key: + (minNode, lc) = self.delete_min() + return lc else: - self.unlink_node(node)#helper function - - subheap=PairingHeapStandard(node) - subheap.delete_min() - self.merge(subheap) - print("result is {}".format(self.listInorder(self.root))) - pass + self.unlink_node(node) # helper function + + subheap = PairingHeapStandard(node) + linkCount = subheap.delete_min() + linkCount += self.merge(subheap) + return linkCount def unlink_node(self, node): - #removes node from heap updating pointers - if self.root==node:#remove the whole heap - self.root=None + """removes node from heap, updating pointers accordingly""" + if self.root == node: # remove the whole heap + self.root = None else: - if node.nextSibling!=None: - temp=node.nextSibling - while temp.nextSibling!=None: - temp=temp.nextSibling - if temp.parent.leftChild==node:#node is leftmost child - #link parent to next sibling - temp.parent.leftChild=node.nextSibling - node.nextSibling=None + if node.nextSibling != None: + temp = node.nextSibling + while temp.nextSibling != None: + temp = temp.nextSibling + if temp.parent.leftChild == node: # node is leftmost child + # link parent to next sibling + temp.parent.leftChild = node.nextSibling + node.nextSibling = None else: - #node is neither first nor last child of parent - prevSibling=temp.parent.leftChild - while prevSibling.nextSibling!=node:#find left (previous) sibling - prevSibling=prevSibling.nextSibling - prevSibling.nextSibling=node.nextSibling #cut out node, link left and right sibling + # node is neither first nor last child of parent + prevSibling = temp.parent.leftChild + while prevSibling.nextSibling != node: # find left (previous) sibling + prevSibling = prevSibling.nextSibling + prevSibling.nextSibling = node.nextSibling # cut out node, link left and right sibling else: - #node is rightmost child of parent - if node.parent.leftChild==node: - #node is only child: just remove - node.parent.leftChild=None + # node is rightmost child of parent + if node.parent.leftChild == node: + # node is only child: just remove + node.parent.leftChild = None else: - prevSibling=node.parent.leftChild - while prevSibling.nextSibling!=node:#find left (previous) sibling - prevSibling=prevSibling.nextSibling - prevSibling.parent=node.parent - prevSibling.nextSibling=None - node.parent=None - - + prevSibling = node.parent.leftChild + while prevSibling.nextSibling != node: # find left (previous) sibling + prevSibling = prevSibling.nextSibling + prevSibling.parent = node.parent + prevSibling.nextSibling = None + node.parent = None diff --git a/paper-dijkstra-test-new.py b/scripts/paper-dijkstra-test-new.py similarity index 91% rename from paper-dijkstra-test-new.py rename to scripts/paper-dijkstra-test-new.py index 412551919286766960e526ed4c77236f52c223ca..a467324ceb1026435900439554e8b273c488c8a0 100755 --- a/paper-dijkstra-test-new.py +++ b/scripts/paper-dijkstra-test-new.py @@ -1,12 +1,19 @@ #!/usr/bin/python3 +"""Experimental script comparing performance of pairing heap and smooth heap +as priority queue in Dijkstra's algorithm. Algorithm is run on randomly generated +Erdös-Renyi graphs of fixed size and variable edge probability. +Results are stored as .csv files in ../data folder and plots of results in ../plots""" + + +import os, sys, inspect +# ensuring imports work +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) from node import Node -from pairing_heap_interface import PairingHeapInterface from pairing_heap import PairingHeap import networkx as nx -import sys import random -import math -import numpy as np import matplotlib.pyplot as plt import os import psutil @@ -81,7 +88,7 @@ def plot_avg_counts(avgCounts): #plt.gca().invert_xaxis() figure = plt.gcf() # get current figure figure.set_size_inches(16, 18) # set figure's size manually to full screen - plt.savefig('plots/paper-dijkstra-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces + plt.savefig('../plots/paper-dijkstra-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces plt.legend(loc='best') plt.show() @@ -89,14 +96,14 @@ def plot_avg_counts(avgCounts): def export_results(xs, results, countType, heapTypes, filename="dijkstra"): # parse data as randomness parameter; counts per heap type if countType == COUNT_TYPE_BOTH: - with open("data/" + filename + '-comps.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-comps.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) for i in range(len(results[0])): row = [xs[i]] + [results[0][i][k] for k in TYPES.keys()] csvwriter.writerow(row) - with open("data/" + filename + '-links.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-links.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) @@ -104,7 +111,7 @@ def export_results(xs, results, countType, heapTypes, filename="dijkstra"): row = [xs[i]] + [results[1][i][k] for k in TYPES.keys()] csvwriter.writerow(row) else: - fn = "data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "data/" + filename + '-comps.csv' + fn = "../data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "../data/" + filename + '-comps.csv' with open(fn, 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) diff --git a/paper-dijkstra-test2-new.py b/scripts/paper-dijkstra-test2-new.py similarity index 88% rename from paper-dijkstra-test2-new.py rename to scripts/paper-dijkstra-test2-new.py index 9f71a2c14c683e9ec3d170782bf23389246267d4..532e1b74857286524f75ea0fe057ead905ec54e7 100755 --- a/paper-dijkstra-test2-new.py +++ b/scripts/paper-dijkstra-test2-new.py @@ -1,14 +1,20 @@ #!/usr/bin/python3 +"""Experimental script comparing performance of pairing heap and smooth heap +as priority queue in Dijkstra's algorithm. Algorithm is run on randomly generated +10-regular graphs of variable size. +Results are stored as .csv files in ../data folder and plots of results in ../plots""" + + +import os, sys, inspect +# ensuring imports from parent directory work +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) from node import Node -from pairing_heap_interface import PairingHeapInterface from pairing_heap import PairingHeap import networkx as nx -import sys import random -import math -import numpy as np import matplotlib.pyplot as plt -import os import psutil import csv @@ -18,27 +24,21 @@ COUNT_TYPE_COMPS = -2 TYPES = {21: "Pairing", 22: "Smooth"} MAX_TYPE_KEY = max(TYPES.keys()) -#COLOURS = ['xkcd:fire engine red', 'xkcd:dusty orange', 'xkcd:clear blue', 'xkcd:cool green', -# 'xkcd:macaroni and cheese', 'xkcd:fire engine red', 'xkcd:dusty orange', 'xkcd:clear blue', -# 'xkcd:cool green', 'xkcd:macaroni and cheese', 'xkcd:bright sky blue', 'xkcd:bright sky blue', 'xkcd:green', -# 'xkcd:ochre', 'xkcd:sea blue', 'xkcd:sea green', 'xkcd:sea blue', 'xkcd:warm grey', -# 'xkcd:bright sky blue', 'xkcd:bright sky blue'] LINETYPES = 5 * ['-'] + 5 * ['--'] + ['--', '-'] + ['--', '--', '--', '--', '--', '--', '-', '-'] FIG_LABELS = ["comparisons", "links"] MAX_TYPE_KEY = max(TYPES.keys()) +# colours from https://xkcd.com/color/rgb/ COLOURS = {21:'xkcd:fire engine red', 22:'xkcd:sea green'} SHADE_COLOURS = {21:'#fe4d4e', 22:'#58ab8e'} NUMBER_TESTS = 10 # number of tests to run -TEST_SIZE = 500 # ,6000,7000,8000,9000,10000,20000,30000,40000,50000,60000,70000,80000,90000,100000 +TEST_SIZE = 500 EDGE_PROBABILITY = 0.05 WEIGHT_RANGE = 10000 def plot_avg_counts_old(avgCounts): - # colours from https://xkcd.com/color/rgb/ - linetypes = 5 * ["-"] + 5 * ["--"] + ["--", "-"] + ["-, -"] plt.figure('Dijkstra with variable connectivity') for k in TYPES.keys(): avgComps = [acounts[k] for acounts in avgCounts[0]] @@ -52,7 +52,6 @@ def plot_avg_counts_old(avgCounts): plt.show() def plot_avg_counts(avgCounts): - # colours from https://xkcd.com/color/rgb/ MARKERS_COMP = {21:"o", 12:"d", 22:"^"}#https://matplotlib.org/3.1.1/api/markers_api.html MARKERS_LINK = {21:"o", 12:"D", 22:"D"} plt.figure('avg number of operations in Dijkstra\'s algorithm') @@ -70,18 +69,18 @@ def plot_avg_counts(avgCounts): plt.plot(deviations, avgLinks, color=COLOURS[k], linestyle="--", marker=MARKERS_LINK[k], markerfacecolor=COLOURS[k], markersize=9, markeredgewidth=1, markeredgecolor='black', label=TYPES[k] + " links") plt.fill_between(deviations, minLinks, maxLinks, color=SHADE_COLOURS[k], alpha=.3) - plt.xlabel('Graph size', fontsize=26) plt.ylabel('Avg. number of operations / size', fontsize=26) plt.xticks(fontsize=20) plt.yticks(fontsize=20) plt.rc('legend',fontsize=26) # using a size in points + #plt.gca().invert_xaxis() plt.legend() plt.grid(True) #plt.gca().invert_xaxis() figure = plt.gcf() # get current figure figure.set_size_inches(16, 18) # set figure's size manually to full screen - plt.savefig('plots/paper-dijkstra2-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces + plt.savefig('../plots/paper-dijkstra2-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces plt.legend(loc='best') plt.show() @@ -89,14 +88,14 @@ def plot_avg_counts(avgCounts): def export_results(xs, results, countType, heapTypes, filename="dijkstra2"): # parse data as randomness parameter; counts per heap type if countType == COUNT_TYPE_BOTH: - with open("data/" + filename + '-comps.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-comps.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) for i in range(len(results[0])): row = [xs[i]] + [results[0][i][k] for k in TYPES.keys()] csvwriter.writerow(row) - with open("data/" + filename + '-links.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-links.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) @@ -104,7 +103,7 @@ def export_results(xs, results, countType, heapTypes, filename="dijkstra2"): row = [xs[i]] + [results[1][i][k] for k in TYPES.keys()] csvwriter.writerow(row) else: - fn = "data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "data/" + filename + '-comps.csv' + fn = "../data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "../data/" + filename + '-comps.csv' with open(fn, 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) diff --git a/paper-permutations.py b/scripts/paper-permutations.py similarity index 94% rename from paper-permutations.py rename to scripts/paper-permutations.py index e384830e2a6337afa30e9f26875d452789e18b31..5d8b956ba4576f159ac1be774815ea1cb11bcc02 100755 --- a/paper-permutations.py +++ b/scripts/paper-permutations.py @@ -5,7 +5,8 @@ import matplotlib.pyplot as plt import math import numpy as np -"""This file generates visualizations of the different random permutation classes""" +"""This file generates visualizations of the different random permutation classes. +Resulting plots are stored in ../plots""" def plot_permutation(permutation, title, filename): # visualizing given permutation as element index in permutation over element index in sorted list @@ -25,7 +26,7 @@ def plot_permutation(permutation, title, filename): plt.grid(True) figure = plt.gcf() # get current figure figure.set_size_inches(16, 18) # set figure's size manually to full screen - plt.savefig('plots/paper-permutation-{}.svg'.format(filename), bbox_inches='tight') # bbox_inches removes extra white spaces + plt.savefig('../plots/paper-permutation-{}.svg'.format(filename), bbox_inches='tight') # bbox_inches removes extra white spaces plt.legend(loc='best') plt.show() diff --git a/paper-sorting-loc-new.py b/scripts/paper-sorting-loc-new.py similarity index 87% rename from paper-sorting-loc-new.py rename to scripts/paper-sorting-loc-new.py index 994df0b32be8b044374b833e265641f2a2da1cef..403323e8cf59276d624b26d85e0d317297448e08 100644 --- a/paper-sorting-loc-new.py +++ b/scripts/paper-sorting-loc-new.py @@ -1,13 +1,20 @@ #!/usr/bin/python3 -from random import shuffle -import random +"""Experimental script comparing performance of pairing heap and smooth heap +in 'sorting mode': n inserts followed by n delete-min operations. +Input lists are randomly generated 'localised' permutations of fixed length with variable +locality parameter. +Results are stored as .csv files in ../data folder and plots of results in ../plots""" + + import numpy as np import matplotlib.pyplot as plt -import sys -import signal -import copy import math import csv +import os, sys, inspect +# ensuring imports work +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) from node import Node from pairing_heap import PairingHeap @@ -74,24 +81,24 @@ def plot_avg_counts(avgCounts): plt.grid(True) figure = plt.gcf() # get current figure figure.set_size_inches(16, 18) # set figure's size manually to your full screen (32x18) - plt.savefig('plots/paper-sorting-loc-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces + plt.savefig('../plots/paper-sorting-loc-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces plt.legend(loc='best') plt.show() def export_results(params, results, countType, heapTypes, filename="dijkstra"): - # exports results of simulation as separate .csv files, one for links and one for comparisons, into /data directory + # exports results of simulation as separate .csv files, one for links and one for comparisons, into ../data directory # each row contains randomness parameter value; plus one column containing the number of operations for each heap type if countType == COUNT_TYPE_BOTH: - with open("data/" + filename + '-comps.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-comps.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) for i in range(len(results[0])): row = [params[i]] + [results[0][i][k] for k in TYPES.keys()] csvwriter.writerow(row) - with open("data/" + filename + '-links.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-links.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) @@ -99,7 +106,7 @@ def export_results(params, results, countType, heapTypes, filename="dijkstra"): row = [params[i]] + [results[1][i][k] for k in TYPES.keys()] csvwriter.writerow(row) else: - fn = "data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "data/" + filename + '-comps.csv' + fn = "../data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "../data/" + filename + '-comps.csv' with open(fn, 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) diff --git a/paper-sorting-sep-new.py b/scripts/paper-sorting-sep-new.py similarity index 88% rename from paper-sorting-sep-new.py rename to scripts/paper-sorting-sep-new.py index 265688c11dd662b34f5ce12feeb67587a1e010e9..48cab31997c06339948105da021356862244bdf0 100644 --- a/paper-sorting-sep-new.py +++ b/scripts/paper-sorting-sep-new.py @@ -1,13 +1,19 @@ #!/usr/bin/python3 -from random import shuffle +"""Experimental script comparing performance of pairing heap and smooth heap +in 'sorting mode': n inserts followed by n delete-min operations. +Input lists are randomly generated separable permutations of variable length. +Results are stored as .csv files in ../data folder and plots of results in ../plots""" + import random -import numpy as np import matplotlib.pyplot as plt -import sys -import signal import copy import math import csv +import os, sys, inspect +# ensuring imports work +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) from node import Node from pairing_heap import PairingHeap @@ -61,23 +67,23 @@ def plot_avg_counts(avgCounts): plt.grid(True) figure = plt.gcf() # get current figure figure.set_size_inches(16, 18) # set figure's size manually to full screen - plt.savefig('plots/paper-sorting-sep-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces + plt.savefig('../plots/paper-sorting-sep-new-test.svg', bbox_inches='tight') # bbox_inches removes extra white spaces plt.legend(loc='best') plt.show() def export_results(params, results, countType, heapTypes, filename="dijkstra"): - # exports results of simulation as separate .csv files, one for links and one for comparisons, into /data directory + # exports results of simulation as separate .csv files, one for links and one for comparisons, into ../data directory # each row contains randomness parameter value; plus one column containing the number of operations for each heap type if countType == COUNT_TYPE_BOTH: - with open("data/" + filename + '-comps.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-comps-test.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) for i in range(len(results[0])): row = [params[i]] + [results[0][i][k] for k in TYPES.keys()] csvwriter.writerow(row) - with open("data/" + filename + '-links.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-links-test.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) @@ -85,7 +91,7 @@ def export_results(params, results, countType, heapTypes, filename="dijkstra"): row = [params[i]] + [results[1][i][k] for k in TYPES.keys()] csvwriter.writerow(row) else: - fn = "data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "data/" + filename + '-comps.csv' + fn = "../data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "../data/" + filename + '-comps.csv' with open(fn, 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) diff --git a/paper-sorting-subseq-new.py b/scripts/paper-sorting-subseq-new.py similarity index 87% rename from paper-sorting-subseq-new.py rename to scripts/paper-sorting-subseq-new.py index a72bc084f07da6f06dfea08d526c68e4ae7924cf..2db58de98c79ad1a46587b3c534ed23feb02c3e1 100644 --- a/paper-sorting-subseq-new.py +++ b/scripts/paper-sorting-subseq-new.py @@ -1,18 +1,24 @@ #!/usr/bin/python3 +"""Experimental script comparing performance of pairing heap and smooth heap +in 'sorting mode': n inserts followed by n delete-min operations. +Input lists are randomly generated lists of fixed length, containing sorted subsequences +of variable average length. +Results are stored as .csv files in ../data folder and plots of results in ../plots""" + + from random import shuffle import random -import numpy as np import matplotlib.pyplot as plt -import sys -import signal import copy import math import csv +import os, sys, inspect +# ensuring imports work +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) from node import Node from pairing_heap import PairingHeap -from pairing_heap_interface import PairingHeapInterface -from pairing_heap_standard import PairingHeapStandard -from smooth_heap import SmoothHeap COUNT_TYPE_BOTH = 0 COUNT_TYPE_LINKS = -1 @@ -79,23 +85,23 @@ def plot_avg_counts(avgCounts): plt.gca().invert_xaxis() figure = plt.gcf() # get current figure figure.set_size_inches(16, 18) # set figure's size manually to full screen - plt.savefig('plots/paper-sorting-subseq-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces + plt.savefig('../plots/paper-sorting-subseq-new.svg', bbox_inches='tight') # bbox_inches removes extra white spaces plt.legend(loc='best') plt.show() def export_results(params, results, countType, heapTypes, filename="dijkstra"): - # exports results of simulation as separate .csv files, one for links and one for comparisons, into /data directory + # exports results of simulation as separate .csv files, one for links and one for comparisons, into ../data directory # each row contains randomness parameter value; plus one column containing the number of operations for each heap type if countType == COUNT_TYPE_BOTH: - with open("data/" + filename + '-comps.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-comps.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) for i in range(len(results[0])): row = [params[i]] + [results[0][i][k] for k in TYPES.keys()] csvwriter.writerow(row) - with open("data/" + filename + '-links.csv', 'w', newline='') as csvfile: + with open("../data/" + filename + '-links.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.keys()]) @@ -103,7 +109,7 @@ def export_results(params, results, countType, heapTypes, filename="dijkstra"): row = [params[i]] + [results[1][i][k] for k in TYPES.keys()] csvwriter.writerow(row) else: - fn = "data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "data/" + filename + '-comps.csv' + fn = "../data/" + filename + '-links.csv' if countType == COUNT_TYPE_LINKS else "../data/" + filename + '-comps.csv' with open(fn, 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', quotechar='|', quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["randomness parameter value"] + [name for name in TYPES.values()]) diff --git a/smooth_heap.py b/smooth_heap.py index 2983981f17482f6326b97935edbda2ce819f03a0..fa3ebce236d4ea1d35dabb98c4e45620a7b97f16 100644 --- a/smooth_heap.py +++ b/smooth_heap.py @@ -3,311 +3,314 @@ from node import Node import math from pairing_heap_interface import PairingHeapInterface + class SmoothHeap(PairingHeapInterface): - forest=[] #list storing roots of all top-level trees not in buffer - buffer=[] #decrease buffer - minNode=None - size=0 - - def __init__(self, root=None): - self.forest=[] - self.buffer=[] - if root!=None: - root.parent=None - root.nextSibling=root - self.minNode=root - self.forest+=[root] - #self.size=0 - - def make_heap(self): - #this is equivalent to init - pass - - def find_min(self): - return self.minNode - - def listPreOrderHelper(self, root): - res=[] - if root.rightChild==None: - return [root.key] - else: - current=root.rightChild - res+=[root.key, self.listPreOrderHelper(current)] - while current.nextSibling!=root.rightChild: - current=current.nextSibling - res+=[self.listPreOrderHelper(current)] - return [res] - - def listPreOrder(self): - res=[] - buf=[] - for item in self.forest: - res+=self.listPreOrderHelper(item) - print(res) - for elem in self.buffer: - buf+=self.listPreOrderHelper(elem) - print("buffer: {}".format(buf)) - - def stable_link_left(self, left, right): - #left node becomes parent of right node - #print("left-linking nodes {} and {}".format(left.key, right.key)) - if left.rightChild!=None: - right.nextSibling = left.rightChild.nextSibling - left.rightChild.nextSibling=right - else: - right.nextSibling = right - left.rightChild=right - right.parent=left - - def stable_link_right(self, left, right): - #right node becomes parent of left node - if right.rightChild==None: - right.rightChild=left - left.nextSibling=left - else: - left.nextSibling=right.rightChild.nextSibling - right.rightChild.nextSibling=left - left.parent=right - - def insert(self, node): - #concatenates node to list of trees in pool - if node==None: - return (0,0)#no comparisons, no links - node.nextSibling=node - node.parent=None - self.forest+=[node] - self.size+=1 - if self.minNode==None or node.key<=self.minNode.key: - self.minNode=node - #print("Result of insert is {}.".format(self.listInorder())) - return (1, 0)#1 comparison, no links - - def merge(self, heap2): - if heap2==None: - return(0,0) - compCount=0 - linkCount=0 - if len(self.forest)+len(self.buffer)>len(heap2.forest)+len(heap2.buffer): - #first heap larger than second - (cc,lc)=heap2.clean_buffer() - linkCount+=lc - compCount+=cc+1#accounting for minNode comparison (at the end) as well - self.forest+=heap2.forest - else: - (cc,lc)=self.clean_buffer() - linkCount+=lc - compCount+=cc+1#accounting for minNode comparison (at the end) as well - self.forest=heap2.forest+self.forest - self.buffer=heap2.buffer - self.size+=heap2.size - if(self.minNode.key>=heap2.minNode.key): - self.minNode=heap2.minNode - return (compCount, linkCount) - - def delete_min(self): - (compCount, linkCount)=self.clean_buffer() - if self.minNode==None or len(self.forest)+len(self.buffer)==0:#this should be the same - return (None, 0, 0) - minKey = self.minNode.key - minKeyNode = self.minNode - minNodeChildren=[] - - if self.minNode.rightChild!=None: - minNodeChildren+=[self.minNode.rightChild] - self.minNode.rightChild.parent=None - current=self.minNode.rightChild.nextSibling - self.minNode.rightChild.nextSibling=self.minNode.rightChild - - while current!=self.minNode.rightChild: - minNodeChildren+=[current] - tempNode = current - current=current.nextSibling - tempNode.nextSibling = tempNode - tempNode.parent = None - idx = self.forest.index(self.minNode)#should we count this somehow? - self.forest = self.forest[:idx]+minNodeChildren+self.forest[idx+1:]#replace minNode with its children - self.size-=1 - (cc, lc)=self.treapify() - return (minKeyNode, compCount+cc, linkCount+lc) - - def treapify(self): - #links roots in pool (forest) into treap and returns number of links/comparisons - #this uses the pseudocode of delete-min from https://arxiv.org/abs/1802.05471 - linkCount=0#counts only number of links - compCount=0#counts only number of comparisons - if len(self.forest)==0: #pool is empty - self.minNode=None - return (compCount, linkCount) - - elif len(self.forest)==1: - self.minNode=self.forest[0] - return (compCount, linkCount) - - else: - i=0 - curr_forest=self.forest - while i<len(curr_forest)-1: - compCount+=1#first if-else comparison - if curr_forest[i].key<curr_forest[i+1].key: - i=i+1 - else: - skip=False - while i>0: - compCount+=1 - linkCount+=1 - if curr_forest[i-1].key>curr_forest[i+1].key: - #stable-link predecessor as parent of current node - self.stable_link_left(curr_forest[i-1], curr_forest[i]) - #remove node at index i from top-list - curr_forest=curr_forest[:i]+curr_forest[i+1:] - i=i-1 - else: - #stable-link successor as parent of current node - self.stable_link_right(curr_forest[i], curr_forest[i+1]) - #remove node at index i from top-list - curr_forest=curr_forest[:i]+curr_forest[i+1:] - #i=i+1 - skip=True - break - if not skip:#i==0 - #stable-link current as leftmost child of successor - self.stable_link_right(curr_forest[i], curr_forest[i+1]) - #remove node from top-list - curr_forest=curr_forest[i+1:] - linkCount+=1 - - while i>0: - #stable-link predecessor as parent of current node - self.stable_link_left(curr_forest[i-1], curr_forest[i]) - curr_forest=curr_forest[:i] - linkCount+=1 - i=i-1 - self.forest=curr_forest - assert len(self.forest)==1 - self.minNode=self.forest[0] - return (compCount, linkCount) - - - def mergesort(self, llist): - #standard mergesort, implemented to count comparisons properly - if len(llist)<2: - return 0, llist - split_idx = int(math.floor(len(llist)/2)) - compsleft, lleft = self.mergesort(llist[:split_idx]) - compsright, lright = self.mergesort(llist[split_idx:]) - l=0 - r=0 - comps = compsleft+compsright - sorted = [] - while l+r<len(llist): - if r==len(lright) or (l<len(lleft) and lleft[l].key<=lright[r].key): - sorted+=[lleft[l]] - l+=1 - else: - sorted+=[lright[r]] - r+=1 - comps+=1 - return comps, sorted - - def clean_buffer(self): - if len(self.buffer)==0:#buffer is empty - return (0,0) - comps,self.buffer = self.mergesort(self.buffer) - self.buffer.reverse() - n=len(self.buffer) - - while len(self.buffer)>1: - self.stable_link_right(self.buffer[0], self.buffer[1]) - self.buffer=self.buffer[1:] - - treapified = self.buffer[0] - self.buffer=[] - (compCount,linkCount)=self.merge(SmoothHeap(treapified)) - - return (compCount+comps, linkCount+n-1) #(n-1)links while consolidating - - def decrease_key(self, node, diff): - assert node!=None - linkCount=0 - compCount=0 - node.key = node.key-diff - #if self.minNode==None or node.key<self.minNode.key: - # self.minNode=node - #compCount+=1 - - if node.parent==None and node.rightChild==None: #node is root and has no children - #could just leave this in place - if node in self.forest: - idx = self.forest.index(node) - self.forest=self.forest[:idx]+self.forest[idx+1:] - self.buffer+=[node] - elif node in self.buffer: - pass - else: - self.listPreOrder() - raise Exception("node with key {} is not in heap".format(node.key)) - - elif node.parent==None: #node is a root and has children - leftChild=node.rightChild.nextSibling - if leftChild.nextSibling!=leftChild: - node.rightChild.nextSibling=leftChild.nextSibling#cut out leftmost child - else: - node.rightChild=None - leftChild.nextSibling=leftChild - leftChild.parent=None - if node in self.forest: - idx = self.forest.index(node)#remove node from pool and replace with leftChild - self.forest = self.forest[:idx] + [leftChild] + self.forest[idx+1:] - self.buffer+=[node] - elif node in self.buffer: - pass - else: - self.listPreOrder() - raise Exception("node with key {} is not in heap".format(node.key)) - else:#node is not a root - leftChild=None - if node.rightChild!=None: - leftChild=node.rightChild.nextSibling - leftChild.parent=node.parent - current=node.parent.rightChild - - if node.nextSibling==node and leftChild!=None:#node not a leaf and has no siblings - if leftChild.nextSibling!=leftChild: - node.rightChild.nextSibling=leftChild.nextSibling#cut out leftmost child - else: - node.rightChild=None - leftChild.nextSibling=leftChild - node.parent.rightChild = leftChild - - elif leftChild!=None:#node is not a leaf and has siblings - if leftChild.nextSibling!=leftChild: - node.rightChild.nextSibling=leftChild.nextSibling#cut out leftmost child - else: - node.rightChild=None - while current.nextSibling!=node:#find predecessor of node - current=current.nextSibling - current.nextSibling=leftChild - leftChild.nextSibling=node.nextSibling - if node.parent.rightChild==node: - node.parent.rightChild=leftChild - - elif node.nextSibling!=node:#node is leaf and has siblings - while current.nextSibling!=node: - current=current.nextSibling - current.nextSibling=node.nextSibling - if node.parent.rightChild==node: - node.parent.rightChild=current - - else: #node is leaf and has no siblings - node.parent.rightChild=None - - node.parent=None - node.nextSibling=node - self.buffer+=[node] - - if len(self.buffer)>math.ceil(math.log(self.size,2)): - #(cc2, lc2)=self.clean_buffer() - (cc2, lc2)=self.clean_buffer() - compCount+=cc2 - linkCount+=lc2 - return (compCount, linkCount) + forest = [] # list storing roots of all top-level trees not in buffer + buffer = [] # decrease buffer + minNode = None + size = 0 + + def __init__(self, root=None): + self.forest = [] + self.buffer = [] + if root is not None: + root.parent = None + root.nextSibling = root + self.minNode = root + self.forest += [root] + + + def make_heap(self): + # this is equivalent to init + pass + + def find_min(self): + return self.minNode + + def listPreOrderHelper(self, root): + res = [] + if root.rightChild is None: + return [root.key] + else: + current = root.rightChild + res += [root.key, self.listPreOrderHelper(current)] + while current.nextSibling != root.rightChild: + current = current.nextSibling + res += [self.listPreOrderHelper(current)] + return [res] + + def listPreOrder(self): + res = [] + buf = [] + for item in self.forest: + res += self.listPreOrderHelper(item) + print(res) + for elem in self.buffer: + buf += self.listPreOrderHelper(elem) + print("buffer: {}".format(buf)) + + def stable_link_left(self, left, right): + """left node becomes parent of right node""" + if left.rightChild is not None: + right.nextSibling = left.rightChild.nextSibling + left.rightChild.nextSibling = right + else: + right.nextSibling = right + left.rightChild = right + right.parent = left + + def stable_link_right(self, left, right): + """right node becomes parent of left node""" + if right.rightChild is None: + right.rightChild = left + left.nextSibling = left + else: + left.nextSibling = right.rightChild.nextSibling + right.rightChild.nextSibling = left + left.parent = right + + def insert(self, node): + """concatenates node to list of roots in forest list""" + if node is None: + return (0, 0) # no comparisons, no links + node.nextSibling = node + node.parent = None + self.forest += [node] + self.size += 1 + if self.minNode is None or node.key <= self.minNode.key: + self.minNode = node + return (1, 0) # 1 comparison, no links + + def merge(self, heap2): + """cleans buffer of smaller heap, then concatenates forest lists + returns number of comparisons and link operations""" + if heap2 is None: + return (0, 0) + compCount = 0 + linkCount = 0 + if len(self.forest) + len(self.buffer) > len(heap2.forest) + len(heap2.buffer): + # first heap larger than second + (cc, lc) = heap2.clean_buffer() + linkCount += lc + compCount += cc + 1 # accounting for minNode comparison (at the end) as well + self.forest += heap2.forest + else: + (cc, lc) = self.clean_buffer() + linkCount += lc + compCount += cc + 1 # accounting for minNode comparison (at the end) as well + self.forest = heap2.forest + self.forest + self.buffer = heap2.buffer + self.size += heap2.size + if (self.minNode.key >= heap2.minNode.key): + self.minNode = heap2.minNode + return (compCount, linkCount) + + def delete_min(self): + """consolidates and empties buffer, replaces minimum by its children in root list, + then consolidates root list. + Returns minNode, number of comparisons, number of link operations""" + (compCount, linkCount) = self.clean_buffer() + if self.minNode is None or len(self.forest) + len(self.buffer) == 0: # this should be the same + return (None, 0, 0) + minKeyNode = self.minNode + minNodeChildren = [] + + if self.minNode.rightChild is not None: + minNodeChildren += [self.minNode.rightChild] + self.minNode.rightChild.parent = None + current = self.minNode.rightChild.nextSibling + self.minNode.rightChild.nextSibling = self.minNode.rightChild + + while current != self.minNode.rightChild: + minNodeChildren += [current] + tempNode = current + current = current.nextSibling + tempNode.nextSibling = tempNode + tempNode.parent = None + idx = self.forest.index(self.minNode) + self.forest = self.forest[:idx] + minNodeChildren + self.forest[idx + 1:] # replace minNode with its children + self.size -= 1 + (cc, lc) = self.treapify() + return (minKeyNode, compCount + cc, linkCount + lc) + + def treapify(self): + """links roots in pool (forest) into treap + (this uses the pseudocode of delete-min from https://arxiv.org/abs/1802.05471) + returns number of links/comparisons + """ + linkCount = 0 # counts only number of links + compCount = 0 # counts only number of comparisons + if len(self.forest) == 0: # pool is empty + self.minNode = None + return (compCount, linkCount) + + elif len(self.forest) == 1: + self.minNode = self.forest[0] + return (compCount, linkCount) + + else: + i = 0 + curr_forest = self.forest + while i < len(curr_forest) - 1: + compCount += 1 # first if-else comparison + if curr_forest[i].key < curr_forest[i + 1].key: + i = i + 1 + else: + skip = False + while i > 0: + compCount += 1 + linkCount += 1 + if curr_forest[i - 1].key > curr_forest[i + 1].key: + # stable-link predecessor as parent of current node + self.stable_link_left(curr_forest[i - 1], curr_forest[i]) + # remove node at index i from top-list + curr_forest = curr_forest[:i] + curr_forest[i + 1:] + i = i - 1 + else: + # stable-link successor as parent of current node + self.stable_link_right(curr_forest[i], curr_forest[i + 1]) + # remove node at index i from top-list + curr_forest = curr_forest[:i] + curr_forest[i + 1:] + # i=i+1 + skip = True + break + if not skip: # i==0 + # stable-link current as leftmost child of successor + self.stable_link_right(curr_forest[i], curr_forest[i + 1]) + # remove node from top-list + curr_forest = curr_forest[i + 1:] + linkCount += 1 + + while i > 0: + # stable-link predecessor as parent of current node + self.stable_link_left(curr_forest[i - 1], curr_forest[i]) + curr_forest = curr_forest[:i] + linkCount += 1 + i = i - 1 + self.forest = curr_forest + assert len(self.forest) == 1 + self.minNode = self.forest[0] + return (compCount, linkCount) + + def mergesort(self, llist): + # standard mergesort, implemented to count comparisons properly + if len(llist) < 2: + return 0, llist + split_idx = int(math.floor(len(llist) / 2)) + compsleft, lleft = self.mergesort(llist[:split_idx]) + compsright, lright = self.mergesort(llist[split_idx:]) + l = 0 + r = 0 + comps = compsleft + compsright + sorted = [] + while l + r < len(llist): + if r == len(lright) or (l < len(lleft) and lleft[l].key <= lright[r].key): + sorted += [lleft[l]] + l += 1 + else: + sorted += [lright[r]] + r += 1 + comps += 1 + return comps, sorted + + def clean_buffer(self): + if len(self.buffer) == 0: # buffer is empty + return (0, 0) + comps, self.buffer = self.mergesort(self.buffer) + self.buffer.reverse() + n = len(self.buffer) + + while len(self.buffer) > 1: + self.stable_link_right(self.buffer[0], self.buffer[1]) + self.buffer = self.buffer[1:] + + treapified = self.buffer[0] + self.buffer = [] + (compCount, linkCount) = self.merge(SmoothHeap(treapified)) + + return (compCount + comps, linkCount + n - 1) # (n-1)links while consolidating + + def decrease_key(self, node, diff): + """cut out node from current location, leaving leftmost child; + decrease key; place node with remaining subtree in buffer. + Returns number of comparisons, links""" + assert node is not None + linkCount = 0 + compCount = 0 + node.key = node.key - diff + + if node.parent is None and node.rightChild is None: # node is root and has no children + # could just leave this in place alternatively + if node in self.forest: + idx = self.forest.index(node) + self.forest = self.forest[:idx] + self.forest[idx + 1:] + self.buffer += [node] + elif node in self.buffer: + pass + else: + self.listPreOrder() + raise Exception("node with key {} is not in heap".format(node.key)) + + elif node.parent is None: # node is a root and has children + leftChild = node.rightChild.nextSibling + if leftChild.nextSibling != leftChild: + node.rightChild.nextSibling = leftChild.nextSibling # cut out leftmost child + else: + node.rightChild = None + leftChild.nextSibling = leftChild + leftChild.parent = None + if node in self.forest: + idx = self.forest.index(node) # remove node from pool and replace with leftChild + self.forest = self.forest[:idx] + [leftChild] + self.forest[idx + 1:] + self.buffer += [node] + elif node in self.buffer: + pass + else: + self.listPreOrder() + raise Exception("node with key {} is not in heap".format(node.key)) + else: # node is not a root + leftChild = None + if node.rightChild is not None: + leftChild = node.rightChild.nextSibling + leftChild.parent = node.parent + current = node.parent.rightChild + + if node.nextSibling == node and leftChild is not None: # node not a leaf and has no siblings + if leftChild.nextSibling != leftChild: + node.rightChild.nextSibling = leftChild.nextSibling # cut out leftmost child + else: + node.rightChild = None + leftChild.nextSibling = leftChild + node.parent.rightChild = leftChild + + elif leftChild is not None: # node is not a leaf and has siblings + if leftChild.nextSibling != leftChild: + node.rightChild.nextSibling = leftChild.nextSibling # cut out leftmost child + else: + node.rightChild = None + while current.nextSibling != node: # find predecessor of node + current = current.nextSibling + current.nextSibling = leftChild + leftChild.nextSibling = node.nextSibling + if node.parent.rightChild == node: + node.parent.rightChild = leftChild + + elif node.nextSibling != node: # node is leaf and has siblings + while current.nextSibling != node: + current = current.nextSibling + current.nextSibling = node.nextSibling + if node.parent.rightChild == node: + node.parent.rightChild = current + + else: # node is leaf and has no siblings + node.parent.rightChild = None + + node.parent = None + node.nextSibling = node + self.buffer += [node] + + if len(self.buffer) > math.ceil(math.log(self.size, 2)): + (cc2, lc2) = self.clean_buffer() + compCount += cc2 + linkCount += lc2 + return (compCount, linkCount) diff --git a/smooth_heap_l.py b/smooth_heap_l.py index 0c62c78bbe63370868a9f392e8eaac97e1ce05ae..5b7bc6572c7c489688559dae088ab8be7287a943 100644 --- a/smooth_heap_l.py +++ b/smooth_heap_l.py @@ -3,224 +3,202 @@ from node import Node import math from pairing_heap_interface import PairingHeapInterface + class SmoothHeapL(PairingHeapInterface): - forest=[] #list storing roots of all top-level trees not in buffer - minNode=None - - def __init__(self, root=None): - self.forest=[] - if root!=None: - root.parent=None - root.nextSibling=root - self.minNode=root - self.forest+=[root] - - def make_heap(self): - #this is equivalent to init - pass - - def find_min(self): - return self.minNode - - def listPreOrderHelper(self, root): - res=[] - if root.rightChild==None: - return [root.key] - else: - current=root.rightChild - res+=[root.key, self.listPreOrderHelper(current)] - while current.nextSibling!=root.rightChild: - current=current.nextSibling - res+=[self.listPreOrderHelper(current)] - return [res] - - def listPreOrder(self): - res=[] - buf=[] - for item in self.forest: - res+=self.listPreOrderHelper(item) - print(res) - - def stable_link_left(self, left, right): - #left node becomes parent of right node - #print("left-linking nodes {} and {}".format(left.key, right.key)) - if left.rightChild!=None: - right.nextSibling = left.rightChild.nextSibling - left.rightChild.nextSibling=right - else: - right.nextSibling = right - left.rightChild=right - right.parent=left - - def stable_link_right(self, left, right): - #right node becomes parent of left node - if right.rightChild==None: - right.rightChild=left - left.nextSibling=left - else: - left.nextSibling=right.rightChild.nextSibling - right.rightChild.nextSibling=left - left.parent=right - - def insert(self, node): - #concatenates node to list of trees in pool - if node==None: - return (0,0)#no comparisons, no links - node.nextSibling=node - node.parent=None - self.forest+=[node] - #if self.minNode==None or node.key<self.minNode.key: - # self.minNode=node - #print("Result of insert is {}.".format(self.listInorder())) - return (0, 0)#1 comparison, no links - - def merge(self, heap2): - if heap2==None: - return(0,0) - compCount=0 - linkCount=0 - if len(self.forest)>len(heap2.forest): - #first heap larger than second - self.forest+=heap2.forest - else: - self.forest=heap2.forest+self.forest - #if(self.minNode.key>heap2.minNode.key): - # self.minNode=heap2.minNode - return (compCount, linkCount)#TODO add 1 if comparing minnode - - def delete_min(self): - if (len(self.forest)==0): - return(None,0,0) - (cc, lc)=self.treapify() - assert len(self.forest)==1 - minKey = self.minNode.key - minKeyNode = self.minNode - minNodeChildren=[] - - if self.minNode.rightChild!=None: - minNodeChildren+=[self.minNode.rightChild] - self.minNode.rightChild.parent=None - current=self.minNode.rightChild.nextSibling - self.minNode.rightChild.nextSibling=self.minNode.rightChild - - while current!=self.minNode.rightChild: - minNodeChildren+=[current] - tempNode = current - current=current.nextSibling - tempNode.nextSibling = tempNode - tempNode.parent = None - self.forest = minNodeChildren - #print('###') - #print(minKeyNode.key) - return (minKeyNode, cc, lc) - - def treapify(self): - #links roots in pool (forest) into treap and returns number of links/comparisons - #this uses the pseudocode of delete-min from https://arxiv.org/abs/1802.05471 - linkCount=0#counts only number of links - compCount=0#counts only number of comparisons - fs = len(self.forest) - if len(self.forest)==0: #pool is empty - self.minNode=None - return (compCount, linkCount) - - elif len(self.forest)==1: - self.minNode=self.forest[0] - return (compCount, linkCount) - - else: - i=0 - curr_forest=self.forest - while i<len(curr_forest)-1: - compCount+=1#first if-else comparison - if curr_forest[i].key<curr_forest[i+1].key: - i=i+1 - else: - skip=False - while i>0: - compCount+=1 - linkCount+=1 - if curr_forest[i-1].key>curr_forest[i+1].key: - #stable-link predecessor as parent of current node - self.stable_link_left(curr_forest[i-1], curr_forest[i]) - #remove node at index i from top-list - curr_forest=curr_forest[:i]+curr_forest[i+1:] - i=i-1 - else: - #stable-link successor as parent of current node - self.stable_link_right(curr_forest[i], curr_forest[i+1]) - #remove node at index i from top-list - curr_forest=curr_forest[:i]+curr_forest[i+1:] - #i=i+1 - skip=True - break - if not skip:#i==0 - #stable-link current as leftmost child of successor - self.stable_link_right(curr_forest[i], curr_forest[i+1]) - #remove node from top-list - curr_forest=curr_forest[i+1:] - linkCount+=1 - - while i>0: - #stable-link predecessor as parent of current node - self.stable_link_left(curr_forest[i-1], curr_forest[i]) - curr_forest=curr_forest[:i] - linkCount+=1 - i=i-1 - self.forest=curr_forest - assert len(self.forest)==1 - self.minNode=self.forest[0] - assert(fs-1 == linkCount) - return (compCount, linkCount) - - - def mergesort(self, llist): - #standard mergesort, implemented to count comparisons properly - if len(llist)<2: - return 0, llist - split_idx = int(math.floor(len(llist)/2)) - compsleft, lleft = self.mergesort(llist[:split_idx]) - compsright, lright = self.mergesort(llist[split_idx:]) - l=0 - r=0 - comps = compsleft+compsright - sorted = [] - while l+r<len(llist): - if r==len(lright) or (l<len(lleft) and lleft[l].key<=lright[r].key): - sorted+=[lleft[l]] - l+=1 - else: - sorted+=[lright[r]] - r+=1 - comps+=1 - return comps, sorted - - def decrease_key(self, node, diff): - assert node!=None - node.key = node.key-diff - - #concatenates node to list of trees in pool - - if node.parent==None: #node is a root and has children - if node in self.forest: - pass #leave in-place - else: - self.listPreOrder() - raise Exception("node with key {} is not in heap".format(node.key)) - else:#node is not a root - - if node.nextSibling==node:#node has no siblings - node.parent.rightChild=None - - else:#node has siblings - current=node.nextSibling - while current.nextSibling!=node:#find predecessor of node - current=current.nextSibling - current.nextSibling=node.nextSibling - if node.parent.rightChild==node: - node.parent.rightChild=current - - node.parent=None - node.nextSibling=node - self.forest+=[node] - return (0, 0) + """lazy implementation of smooth heap without buffer""" + forest = [] # list storing roots of all top-level trees + minNode = None + + def __init__(self, root=None): + self.forest = [] + if root != None: + root.parent = None + root.nextSibling = root + self.minNode = root + self.forest += [root] + + def make_heap(self): + # this is equivalent to init + pass + + def find_min(self): + return self.minNode + + def listPreOrderHelper(self, root): + res = [] + if root.rightChild == None: + return [root.key] + else: + current = root.rightChild + res += [root.key, self.listPreOrderHelper(current)] + while current.nextSibling != root.rightChild: + current = current.nextSibling + res += [self.listPreOrderHelper(current)] + return [res] + + def listPreOrder(self): + res = [] + buf = [] + for item in self.forest: + res += self.listPreOrderHelper(item) + print(res) + + def stable_link_left(self, left, right): + """left node becomes parent of right node""" + if left.rightChild != None: + right.nextSibling = left.rightChild.nextSibling + left.rightChild.nextSibling = right + else: + right.nextSibling = right + left.rightChild = right + right.parent = left + + def stable_link_right(self, left, right): + """right node becomes parent of left node""" + if right.rightChild is None: + right.rightChild = left + left.nextSibling = left + else: + left.nextSibling = right.rightChild.nextSibling + right.rightChild.nextSibling = left + left.parent = right + + def insert(self, node): + """concatenates node to list of trees in pool""" + if node is None: + return (0, 0) # no comparisons, no links + node.nextSibling = node + node.parent = None + self.forest += [node] + return (0, 0) # 1 comparison, no links + + def merge(self, heap2): + """concatenates root lists of heaps""" + if heap2 is None: + return (0, 0) + compCount = 0 + linkCount = 0 + if len(self.forest) > len(heap2.forest): + # first heap larger than second + self.forest += heap2.forest + else: + self.forest = heap2.forest + self.forest + return (compCount, linkCount) + + def delete_min(self): + """consolidates into single tree; extracts min node, + placing its orphaned children in root list. + Returns min node, number comparisons, number links""" + if len(self.forest) == 0: + return (None, 0, 0) + (cc, lc) = self.treapify() + assert len(self.forest) == 1 + minKeyNode = self.minNode + minNodeChildren = [] + + if self.minNode.rightChild is not None: + minNodeChildren += [self.minNode.rightChild] + self.minNode.rightChild.parent = None + current = self.minNode.rightChild.nextSibling + self.minNode.rightChild.nextSibling = self.minNode.rightChild + + while current != self.minNode.rightChild: + minNodeChildren += [current] + tempNode = current + current = current.nextSibling + tempNode.nextSibling = tempNode + tempNode.parent = None + self.forest = minNodeChildren + return (minKeyNode, cc, lc) + + def treapify(self): + """links roots in pool (forest) into treap and returns number of links/comparisons + this uses the pseudocode of delete-min from https://arxiv.org/abs/1802.05471 + returns number comparisons, number link operations performed""" + linkCount = 0 # counts only number of links + compCount = 0 # counts only number of comparisons + fs = len(self.forest) + if len(self.forest) == 0: # pool is empty + self.minNode = None + return (compCount, linkCount) + + elif len(self.forest) == 1: + self.minNode = self.forest[0] + return (compCount, linkCount) + + else: + i = 0 + curr_forest = self.forest + while i < len(curr_forest) - 1: + compCount += 1 # first if-else comparison + if curr_forest[i].key < curr_forest[i + 1].key: + i = i + 1 + else: + skip = False + while i > 0: + compCount += 1 + linkCount += 1 + if curr_forest[i - 1].key > curr_forest[i + 1].key: + # stable-link predecessor as parent of current node + self.stable_link_left(curr_forest[i - 1], curr_forest[i]) + # remove node at index i from top-list + curr_forest = curr_forest[:i] + curr_forest[i + 1:] + i = i - 1 + else: + # stable-link successor as parent of current node + self.stable_link_right(curr_forest[i], curr_forest[i + 1]) + # remove node at index i from top-list + curr_forest = curr_forest[:i] + curr_forest[i + 1:] + # i=i+1 + skip = True + break + if not skip: # i==0 + # stable-link current as leftmost child of successor + self.stable_link_right(curr_forest[i], curr_forest[i + 1]) + # remove node from top-list + curr_forest = curr_forest[i + 1:] + linkCount += 1 + + while i > 0: + # stable-link predecessor as parent of current node + self.stable_link_left(curr_forest[i - 1], curr_forest[i]) + curr_forest = curr_forest[:i] + linkCount += 1 + i = i - 1 + self.forest = curr_forest + assert len(self.forest) == 1 + self.minNode = self.forest[0] + assert (fs - 1 == linkCount) + return (compCount, linkCount) + + + def decrease_key(self, node, diff): + """removes node with subtree from current position; + decreases key; places node in root list.""" + assert node is not None + node.key = node.key - diff + # concatenates node to list of trees in pool + + if node.parent is None: # node is a root and has children + if node in self.forest: + pass # leave in-place + else: + self.listPreOrder() + raise Exception("node with key {} is not in heap".format(node.key)) + else: # node is not a root + + if node.nextSibling == node: # node has no siblings + node.parent.rightChild = None + + else: # node has siblings + current = node.nextSibling + while current.nextSibling != node: # find predecessor of node + current = current.nextSibling + current.nextSibling = node.nextSibling + if node.parent.rightChild == node: + node.parent.rightChild = current + + node.parent = None + node.nextSibling = node + self.forest += [node] + return (0, 0)