PriorityQueue 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +19 -0
- data/README +85 -0
- data/benchmark/dijkstra.rb +169 -0
- data/benchmark/result-CPriorityQueue.gp +8 -0
- data/benchmark/result-CPriorityQueue.png +0 -0
- data/benchmark/result-PoorPriorityQueue.gp +8 -0
- data/benchmark/result-PoorPriorityQueue.png +0 -0
- data/benchmark/result-PriorityQueue.gp +6 -0
- data/benchmark/result-RubyPriorityQueue.gp +8 -0
- data/benchmark/result-RubyPriorityQueue.png +0 -0
- data/benchmark/results.csv +37 -0
- data/benchmark/results.gp +10 -0
- data/benchmark/results.png +0 -0
- data/doc/result-PoorPriorityQueue.png +0 -0
- data/doc/result-PriorityQueue.png +0 -0
- data/doc/result-RubyPriorityQueue.png +0 -0
- data/doc/results.png +0 -0
- data/ext/priority_queue/extconf.rb +2 -0
- data/ext/priority_queue/priority_queue.c +874 -0
- data/lib/priority_queue.rb +14 -0
- data/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/lib/priority_queue/ruby_priority_queue.rb +394 -0
- data/setup.rb +1551 -0
- data/test.rb +11 -0
- data/test/priority_queue_test.rb +358 -0
- metadata +78 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
# A priority queue implementation.
|
2
|
+
# This extension contains two implementations, a c extension and a pure ruby
|
3
|
+
# implementation. When the compiled extension can not be found, it falls back
|
4
|
+
# to the pure ruby extension.
|
5
|
+
#
|
6
|
+
# See CPriorityQueue and RubyPriorityQueue for more information.
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'priority_queue/CPriorityQueue'
|
10
|
+
PriorityQueue = CPriorityQueue
|
11
|
+
rescue LoadError # C Version could not be found, try ruby version
|
12
|
+
require 'priority_queue/ruby_priority_queue'
|
13
|
+
PriorityQueue = RubyPriorityQueue
|
14
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# A Poor mans Priority Queue. (Very inefficent but minimal implemention).
|
2
|
+
class PoorPriorityQueue < Hash
|
3
|
+
def push(object, priority)
|
4
|
+
self[object] = priority
|
5
|
+
end
|
6
|
+
|
7
|
+
def min
|
8
|
+
return nil if self.empty?
|
9
|
+
min_k = self.keys.first
|
10
|
+
min_p = self[min_k]
|
11
|
+
self.each do | k, p |
|
12
|
+
min_k, min_p = k, p if p < min_p
|
13
|
+
end
|
14
|
+
[min_k, min_p]
|
15
|
+
end
|
16
|
+
|
17
|
+
def min_key
|
18
|
+
min[0] rescue nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def min_priority
|
22
|
+
min[1] rescue nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_min
|
26
|
+
return nil if self.empty?
|
27
|
+
min_k, min_p = *min
|
28
|
+
self.delete(min_k)
|
29
|
+
[min_k, min_p]
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_min_return_key
|
33
|
+
delete_min[0] rescue nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_min_return_priority
|
37
|
+
delete_min[1] rescue nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(object)
|
41
|
+
return nil unless self.has_key?(object)
|
42
|
+
result = [object, self[object]]
|
43
|
+
super
|
44
|
+
result
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,394 @@
|
|
1
|
+
# Pure ruby Priority Queue
|
2
|
+
$stdout.sync = true
|
3
|
+
$stderr.sync = true
|
4
|
+
|
5
|
+
class RubyPriorityQueue
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def link_nodes(b1, b2)
|
10
|
+
return link_nodes(b2, b1) if b2.priority < b1.priority
|
11
|
+
|
12
|
+
b2.parent = b1
|
13
|
+
child = b1.child
|
14
|
+
b1.child = b2
|
15
|
+
if child
|
16
|
+
b2.left = child.left
|
17
|
+
b2.left.right = b2
|
18
|
+
b2.right = child
|
19
|
+
child.left = b2
|
20
|
+
else
|
21
|
+
b2.left = b2
|
22
|
+
b2.right = b2
|
23
|
+
end
|
24
|
+
b1.degree += 1
|
25
|
+
b2.mark = false # TODO: Check if this is correct, or if b1 should be marked as false
|
26
|
+
return b1
|
27
|
+
end
|
28
|
+
|
29
|
+
# Does not change length
|
30
|
+
def delete_first
|
31
|
+
return nil unless @rootlist
|
32
|
+
|
33
|
+
result = @rootlist
|
34
|
+
if result == result.right
|
35
|
+
@min = @rootlist = nil
|
36
|
+
else
|
37
|
+
@rootlist = result.right
|
38
|
+
@rootlist.left = result.left
|
39
|
+
@rootlist.left.right = @rootlist
|
40
|
+
|
41
|
+
result.right = result.left = result
|
42
|
+
end
|
43
|
+
return result;
|
44
|
+
end
|
45
|
+
|
46
|
+
def cut_node(n)
|
47
|
+
return self unless n.parent
|
48
|
+
n.parent.degree -= 1
|
49
|
+
if n.parent.child == n
|
50
|
+
if n.right == n
|
51
|
+
n.parent.child = nil
|
52
|
+
else
|
53
|
+
n.parent.child = n.right;
|
54
|
+
end
|
55
|
+
end
|
56
|
+
n.parent = nil
|
57
|
+
n.right.left = n.left
|
58
|
+
n.left.right = n.right
|
59
|
+
|
60
|
+
n.right = @rootlist
|
61
|
+
n.left = @rootlist.left
|
62
|
+
@rootlist.left.right = n
|
63
|
+
@rootlist.left = n
|
64
|
+
|
65
|
+
n.mark = false
|
66
|
+
|
67
|
+
return self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Does not change length priority
|
71
|
+
def insert_tree(tree)
|
72
|
+
if @rootlist == nil
|
73
|
+
@rootlist = @min = tree
|
74
|
+
else
|
75
|
+
l = @rootlist.left
|
76
|
+
l.right = tree
|
77
|
+
@rootlist.left = tree
|
78
|
+
tree.left = l
|
79
|
+
tree.right = @rootlist
|
80
|
+
@min = tree if tree.priority < @min.priority
|
81
|
+
end
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def consolidate
|
86
|
+
return self if self.empty?
|
87
|
+
array_size = (2.0 * Math.log(self.length) / Math.log(2) + 1.0).ceil
|
88
|
+
tree_by_degree = Array.new(array_size)
|
89
|
+
|
90
|
+
while n = delete_first
|
91
|
+
while n1 = tree_by_degree[n.degree]
|
92
|
+
tree_by_degree[n.degree] = nil;
|
93
|
+
n = link_nodes(n, n1);
|
94
|
+
end
|
95
|
+
tree_by_degree[n.degree] = n;
|
96
|
+
end
|
97
|
+
|
98
|
+
@rootlist = @min = nil;
|
99
|
+
tree_by_degree.each do | tree |
|
100
|
+
next unless tree
|
101
|
+
insert_tree(tree)
|
102
|
+
end
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Node class used internally
|
107
|
+
class Node # :nodoc:
|
108
|
+
attr_accessor :parent, :child, :left, :right, :key, :priority, :degree, :mark
|
109
|
+
|
110
|
+
def child=(c)
|
111
|
+
raise "Circular Child" if c == self
|
112
|
+
raise "Child is neighbour" if c == self.right
|
113
|
+
raise "Child is neighbour" if c == self.left
|
114
|
+
@child = c
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_dot(only_down = false, known_nodes = [])
|
118
|
+
p known_nodes.map { | n | n.dot_id }
|
119
|
+
p self.dot_id
|
120
|
+
result = []
|
121
|
+
if only_down
|
122
|
+
raise "Circular #{caller.inspect}" if known_nodes.include?(self)
|
123
|
+
known_nodes << self
|
124
|
+
|
125
|
+
result << "#{dot_id} [label=\"#{@key}: #{@priority}\"];"
|
126
|
+
l = " "
|
127
|
+
#l << "#{@left.dot_id} <- #{dot_id}; " if @left
|
128
|
+
l << "#{dot_id} -> #{@left.dot_id} [constraint=false]; " if @left and @left.dot_id < self.dot_id
|
129
|
+
l << "#{dot_id} -> #{@right.dot_id} [constraint=false];\t\t\t\t/*neighbours*/" if @right and @right.dot_id <= self.dot_id
|
130
|
+
result << l
|
131
|
+
result << " #{dot_id} -> #{@child.dot_id}; //child" if @child
|
132
|
+
result << @child.to_dot(false, known_nodes) if @child
|
133
|
+
else
|
134
|
+
n = self
|
135
|
+
begin
|
136
|
+
result.concat(n.to_dot(true, known_nodes))
|
137
|
+
n = n.right
|
138
|
+
end while n != self
|
139
|
+
end
|
140
|
+
result.flatten.map{|r| " " << r}
|
141
|
+
end
|
142
|
+
|
143
|
+
def dot_id
|
144
|
+
"N#{@key}"
|
145
|
+
end
|
146
|
+
|
147
|
+
def initialize(key, priority)
|
148
|
+
@key = key; @priority = priority; @degree = 0
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
public
|
153
|
+
|
154
|
+
attr_reader :length
|
155
|
+
|
156
|
+
def initialize
|
157
|
+
@nodes = Hash.new
|
158
|
+
@rootlist = nil
|
159
|
+
@min = nil
|
160
|
+
@length = 0
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_dot
|
164
|
+
r = ["digraph fibheap {"]
|
165
|
+
#r << @rootlist.to_dot.join("\n") if @rootlist
|
166
|
+
r << "ROOT -> #{@rootlist.dot_id};" if @rootlist
|
167
|
+
@nodes.to_a.sort.each do | (_, n) |
|
168
|
+
r << " #{n.dot_id} [label=\"#{n.key}: #{n.priority}\"];"
|
169
|
+
r << " #{n.dot_id} -> #{n.right.dot_id} [constraint=false];" if n.right# and n.dot_id < n.right.dot_id
|
170
|
+
r << " #{n.dot_id} -> #{n.left.dot_id} [constraint=false];" if n.left #and n.dot_id < n.left.dot_id
|
171
|
+
r << " #{n.dot_id} -> #{n.child.dot_id}" if n.child
|
172
|
+
end
|
173
|
+
r << "}"
|
174
|
+
r.join("\n")
|
175
|
+
r
|
176
|
+
end
|
177
|
+
|
178
|
+
def display_dot
|
179
|
+
puts to_dot
|
180
|
+
system "echo '#{to_dot}' | twopi -Tps -Groot=ROOT -Goverlap=false> /tmp/dotfile.ps; gv /tmp/dotfile.ps"
|
181
|
+
end
|
182
|
+
|
183
|
+
def change_priority(key, priority)
|
184
|
+
return push(key, priority) unless @nodes[key]
|
185
|
+
|
186
|
+
n = @nodes[key]
|
187
|
+
if n.priority < priority # Priority was increased. Remove the node and reinsert.
|
188
|
+
self.delete(key)
|
189
|
+
self.push(key, priority);
|
190
|
+
return self
|
191
|
+
end
|
192
|
+
n.priority = priority;
|
193
|
+
@min = n if n.priority < @min.priority
|
194
|
+
|
195
|
+
return self if !n.parent or n.parent.priority <= n.priority # Already in rootlist or bigger than parent
|
196
|
+
begin # Cascading Cuts
|
197
|
+
p = n.parent
|
198
|
+
cut_node(n)
|
199
|
+
n = p
|
200
|
+
end while n.mark and n.parent
|
201
|
+
n.mark = true if n.parent
|
202
|
+
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
206
|
+
def push(key, priority)
|
207
|
+
return change_priority(key, priority) if @nodes[key]
|
208
|
+
@nodes[key] = node = Node.new(key, priority)
|
209
|
+
@min = node if !@min or priority < @min.priority
|
210
|
+
if not @rootlist
|
211
|
+
@rootlist = node
|
212
|
+
node.left = node.right = node
|
213
|
+
else
|
214
|
+
node.left = @rootlist.left
|
215
|
+
node.right = @rootlist
|
216
|
+
@rootlist.left.right = node
|
217
|
+
@rootlist.left = node
|
218
|
+
end
|
219
|
+
@length += 1
|
220
|
+
self
|
221
|
+
end
|
222
|
+
|
223
|
+
def empty?
|
224
|
+
@rootlist.nil?
|
225
|
+
end
|
226
|
+
|
227
|
+
def [](key)
|
228
|
+
@nodes[key] and @nodes[key].priority
|
229
|
+
end
|
230
|
+
|
231
|
+
def has_key?(key)
|
232
|
+
@nodes.has_key?(key)
|
233
|
+
end
|
234
|
+
|
235
|
+
alias :[]= :push
|
236
|
+
|
237
|
+
def each
|
238
|
+
@nodes.each do | key, node |
|
239
|
+
yield(key, node.priority)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
include Enumerable
|
244
|
+
|
245
|
+
def min
|
246
|
+
[@min.key, @min.priority] rescue nil
|
247
|
+
end
|
248
|
+
|
249
|
+
def min_key
|
250
|
+
@min.key rescue nil
|
251
|
+
end
|
252
|
+
|
253
|
+
def min_priority
|
254
|
+
@min.priority rescue nil
|
255
|
+
end
|
256
|
+
|
257
|
+
def delete_min_return_key
|
258
|
+
delete_min[0] rescue nil
|
259
|
+
end
|
260
|
+
|
261
|
+
def delete_min_return_priority
|
262
|
+
delete_min[1] rescue nil
|
263
|
+
end
|
264
|
+
|
265
|
+
def delete(key)
|
266
|
+
return nil unless n = @nodes.delete(key)
|
267
|
+
|
268
|
+
if n.child
|
269
|
+
c = n.child
|
270
|
+
e = n.child
|
271
|
+
begin
|
272
|
+
r = c.right
|
273
|
+
cut_node(c)
|
274
|
+
c = r
|
275
|
+
end while c != e
|
276
|
+
end
|
277
|
+
cut_node(n) if n.parent
|
278
|
+
|
279
|
+
if n == n.right
|
280
|
+
@min = nil;
|
281
|
+
@rootlist = nil;
|
282
|
+
else
|
283
|
+
@rootlist = n.right if @rootlist == n
|
284
|
+
if @min == n
|
285
|
+
n1 = n.right
|
286
|
+
@min = n1
|
287
|
+
begin
|
288
|
+
@min = n1 if n1.priority < @min.priority
|
289
|
+
n1 = n1.right
|
290
|
+
end while(n1 != n);
|
291
|
+
end
|
292
|
+
n.right.left = n.left
|
293
|
+
n.left.right = n.right
|
294
|
+
n.left = n
|
295
|
+
n.right = n
|
296
|
+
end
|
297
|
+
@length -= 1
|
298
|
+
return [n.key, n.priority]
|
299
|
+
end
|
300
|
+
|
301
|
+
def delete_min
|
302
|
+
return nil if self.empty?
|
303
|
+
result = self.min
|
304
|
+
|
305
|
+
@nodes.delete(@min.key)
|
306
|
+
|
307
|
+
if @length == 1
|
308
|
+
@rootlist = @min = nil
|
309
|
+
@length = 0
|
310
|
+
else
|
311
|
+
min = @min
|
312
|
+
if @min == @rootlist # If the rootlist is anchored at the minimum, shift to the right
|
313
|
+
if @rootlist == @rootlist.right
|
314
|
+
@rootlist = @min = nil
|
315
|
+
else
|
316
|
+
@rootlist = @min = @min.right
|
317
|
+
end
|
318
|
+
end
|
319
|
+
min.left.right = min.right;
|
320
|
+
min.right.left = min.left;
|
321
|
+
min.left = min.right = min;
|
322
|
+
if min.child
|
323
|
+
# Kinder und Eltern trennen, Markierung aufheben
|
324
|
+
n = min.child;
|
325
|
+
begin
|
326
|
+
n.parent = nil;
|
327
|
+
n.mark = false;
|
328
|
+
n = n.right;
|
329
|
+
end while n != min.child
|
330
|
+
|
331
|
+
# Kinder einf�gen
|
332
|
+
if @rootlist
|
333
|
+
l1 = @rootlist.left
|
334
|
+
l2 = n.left
|
335
|
+
|
336
|
+
l1.right = n
|
337
|
+
n.left = l1
|
338
|
+
l2.right = @rootlist
|
339
|
+
@rootlist.left = l2
|
340
|
+
else
|
341
|
+
@rootlist = n
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# Gr��e anpassen
|
346
|
+
@length -= 1
|
347
|
+
|
348
|
+
# Wieder aufh�bschen
|
349
|
+
consolidate
|
350
|
+
end
|
351
|
+
|
352
|
+
result
|
353
|
+
end
|
354
|
+
|
355
|
+
def inspect
|
356
|
+
"<PriorityQueue: #{@nodes.map{|(_, n)| [n.key, n.priority]}.sort_by{|(_,p)|p}.inspect}>"
|
357
|
+
end
|
358
|
+
|
359
|
+
def initialize_copy(copy)
|
360
|
+
copy_nodes = @nodes
|
361
|
+
@nodes = {}
|
362
|
+
|
363
|
+
copy_nodes.each do | (_, cn) |
|
364
|
+
n = @nodes[cn.key] = Node.new(cn.key, cn.priority)
|
365
|
+
n.mark = cn.mark
|
366
|
+
n.degree = cn.degree
|
367
|
+
end
|
368
|
+
|
369
|
+
copy_nodes.each do | (_, cn) |
|
370
|
+
n = @nodes[cn.key]
|
371
|
+
n.left = @nodes[cn.left.key] if cn.left
|
372
|
+
n.right = @nodes[cn.right.key] if cn.right
|
373
|
+
n.parent = @nodes[cn.parent.key] if cn.parent
|
374
|
+
n.child = @nodes[cn.child.key] if cn.child
|
375
|
+
end
|
376
|
+
@rootlist = @nodes[@rootlist.key] if @rootlist
|
377
|
+
@min = @nodes[@min.key] if @min
|
378
|
+
self
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
if __FILE__ == $0
|
383
|
+
q = RubyPriorityQueue.new
|
384
|
+
|
385
|
+
('a'..'z').each do | n |
|
386
|
+
q[n] = n[0]
|
387
|
+
end
|
388
|
+
q.delete_min
|
389
|
+
q.delete_min
|
390
|
+
q.delete_min
|
391
|
+
q.delete_min
|
392
|
+
q.display_dot
|
393
|
+
q.delete_min
|
394
|
+
end
|