Priority Queue auf Heapbasis

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

hamZta

Administrator

Betreff: Priority Queue auf Heapbasis

BeitragMi, Dez 03, 2008 4:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo!

Ich habe mal aus Langeweile eine Priority Queue auf Heapbasis implementiert.

Was ist das?
Im Endeffekt nichts anderes als ein ein wenig ausgebauter Sortieralgorithmus Wink
Eine "Heap" (dt. "Halde") ist eine Datenstruktur zur Speicherung von Daten mit Schlüsseln. In diesem Fall handelt es sich um einen Binary Tree mit Prioritätswerten als Schlüssel. (wer genaueres wissen will lese hier: Wikipedia "Heap")
Man fügt in seine Heap ein paar "Nodes" (THeapNode) ein und vergibt diesen verschiedene Prioritäten. Dann kann man mit der Methode removeHighest() immer den Node von der Heap holen, der die höchste (d.h. den niedrigsten Prioritätswert, 0 = höchste Priorität) Priorität hat. Dabei wird dieser Node von der Heap gelöscht und der nächste Aufruf von removeHighest() liefert den Node mit der nächsthöheren Priorität. Das geht solange bis keine Nodes mehr auf der Heap liegen.

Ich habe ein kleines Beispiel dazu programmiert:
BlitzMax: [AUSKLAPPEN]
SuperStrict

Import "tpriorityheap.bmx"

'define some priorities
Const P_LOW:Int = 4
Const P_NORMAL:Int = 3
Const P_HIGH:Int = 2
Const P_IMMEDIATE:Int = 1

'extend standard heap node
Type TAction Extends THeapNode

Field _text:String

Function CreateAction:TAction(pr:Int, txt:String)
Local tmpAction:TAction = New TAction
tmpAction._priority = pr
tmpAction._text = txt
Return tmpAction
End Function

Method execute()
Print _text
End Method

End Type

'create my heap
Global myHeap:TPriorityHeap = New TPriorityHeap

'add a few actions
myHeap.addNode(TAction.CreateAction(P_LOW, "Low priority"))
myHeap.addNode(TAction.CreateAction(P_NORMAL, "Normal priority"))
myHeap.addNode(TAction.CreateAction(P_LOW, "Low priority #2"))
myHeap.addNode(TAction.CreateAction(P_HIGH, "High priority"))
myHeap.addNode(TAction.CreateAction(P_IMMEDIATE, "*** URGENT ***"))
myHeap.addNode(TAction.CreateAction(P_NORMAL, "Normal priority"))
myHeap.addNode(TAction.CreateAction(P_IMMEDIATE, "*** URGENT ***"))
myHeap.addNode(TAction.CreateAction(P_HIGH, "High priority"))

'execute these actions
While Not myHeap.isEmpty()
TAction(myHeap.removeHighest()).execute()
Wend

Hier werden erstmal ein paar Prioritäten definiert (muss man natürlich nicht machen, ist aber sauberer ).
Dann folgt der Type TAction der von THeapNode erbt. Dieser erweitert THeapNode um die Methode execute() welche nichts anderes macht, als einen vorher übergebenen Text auszugeben. Jetzt werden ein paar dieser TActions mit verschiedenen Prioritäten erstellt.
Schließlich werden solange TActions von der Heap "ausgeführt" bis keine mehr übrig sind. Das Ergebnis lässt sich leicht erraten:
Code: [AUSKLAPPEN]
*** URGENT ***
*** URGENT ***
High priority
High priority
Normal priority
Normal priority
Low priority #2
Low priority


Wofür kann man das brauchen?
Wie im Beispiel gesehen, werden höhere Prioritäten niederen bevorzugt. Man kann diesen Effekt nutzen um z.B. dringende Nachrichten (übers Netzwerk o.ä.) vor weniger wichtigen zu verschicken. Man kann mit etwas Programmierung wichtigere Methoden öfter als weniger wichtige aufrufen. Sollte BlitzMax später mal ordentlich mit Threads umgehen können könnte man die Queue für eine Priorisierung der Threads nutzen. Oder man benutzt sie ganz einfach zum Sortieren.

Hier nun der eigentliche Code (bitte entschuldigt die englischen Kommentare):

BlitzMax: [AUSKLAPPEN]
Const BLOCK_SIZE:Int = 16

Type TPriorityHeap

Field _data:THeapNode[]
Field _heapPtr:Int

Method New()
_data = New THeapNode[BLOCK_SIZE]
End Method

Rem
Adds a new element to the heap
End Rem

Method add(priority:Int, obj:Object)
_reallocateHeap()

' add the element at the end of the heap
_data[_heapPtr] = THeapNode.Create(priority, obj)
_heapPtr:+1

_siftUp(_heapPtr-1)
End Method

Rem
Adds a new element to the heap
End Rem

Method addNode(node:THeapNode)
_reallocateHeap()

' add the element at the end of the heap

_data[_heapPtr] = node
_heapPtr:+1

_siftUp(_heapPtr-1)
End Method

Rem
Returns true if the heap is empty
End Rem

Method isEmpty:Byte()
Return (_heapPtr = 0)
End Method

Rem
Returns the element with highest priority from the heap
End Rem

Method removeHighest:THeapNode()

' no nodes left
If _heapPtr = 0 Return Null

Local tmpNode:THeapNode = _data[0]
' swap lowest with last heap item
_data[0] = _data[_heapPtr-1]
_heapPtr:-1
' restore heap condition
_heapify(0)
Return tmpNode
End Method

Rem
allocates new space block wise for extra speed
End Rem

Method _reallocateHeap()
If _heapPtr => _data.Length-1
_data = _data[.._data.length+BLOCK_SIZE]
EndIf
End Method

Rem
Restores the heap condition after inserting a new item
End Rem

Method _siftUp(i:Int)
Local father:Int = Floor(i/2)
While i > 0 And _data[i].priority() < _data[father].priority()
Local tmpNode:THeapNode = _data[i]
_data[i] = _data[father]
_data[father] = tmpNode
i = father
father = Floor(i/2)
Wend
End Method

Rem
Heap items with lower priority go down the heap until the heap condition is met
End Rem

Method _heapify(i:Int)
Local leftId:Int = 2*i
Local rightId:Int = 2*i+1
Local smallest:Int = i

If (leftId < _heapPtr) And (_data[leftId].priority() < _data[i].priority())
smallest = leftId
End If

If (rightId < _heapPtr) And (_data[rightId].priority() < _data[smallest].priority())
smallest = rightId
End If

If i <> smallest
Local tmpNode:THeapNode = _data[i]
_data[i] = _data[smallest]
_data[smallest] = tmpNode
_heapify(smallest)
End If
End Method

Rem
DEBUG prints out the heap
End Rem

Method prnt()
For Local i:Int = 0 Until _heapPtr
Print _data[i].priority()
Next
End Method

End Type

Rem
The heap node stores the priority and the associated object
End Rem

Type THeapNode

Field _priority:Int
Field _data:Object

Rem
Creates a new heap node and returns it
End Rem

Function Create:THeapNode(priority:Int, data:Object)
Local tmpNode:THeapNode = New THeapNode
tmpNode._priority = priority
tmpNode._data = data
Return tmpNode
End Function

Method priority:Int()
Return _priority
End Method

Method data:Object()
Return _data
End Method

End Type
Blog.

Firstdeathmaker

BeitragDi, Sep 29, 2009 10:07
Antworten mit Zitat
Benutzer-Profile anzeigen
Cool, war selbst bisher zu faul sowas zu implementieren Wink
Ist aber ne wichtige und praktische Sache, ziemlich elementar.
www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group