depq 0.5 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/lib/depq.rb +155 -42
- data/sample/dijkstra.rb +43 -0
- data/sample/exqsort.rb +90 -0
- data/sample/huffman.rb +37 -0
- data/sample/int-3d.rb +48 -0
- data/sample/kruskal.rb +55 -0
- data/sample/maze.rb +103 -0
- data/sample/prim.rb +78 -0
- data/sample/sched.rb +68 -0
- data/test/test-depq.rb +71 -0
- metadata +10 -2
data/README
CHANGED
@@ -21,7 +21,7 @@ depq is double-ended stable priority queue with priority update operation implem
|
|
21
21
|
* ((<reference manual|URL:http://depq.rubyforge.org/rdoc/classes/Depq.html>))
|
22
22
|
* ((<home page on rubyforge|URL:http://depq.rubyforge.org/>))
|
23
23
|
* ((<project info on rubyforge|URL:http://rubyforge.org/projects/depq/>))
|
24
|
-
* ((<depq on
|
24
|
+
* ((<depq on rubygems.org|URL:http://rubygems.org/gems/depq>))
|
25
25
|
* ((<source repository on github|URL:http://github.com/akr/depq>))
|
26
26
|
* ((<raa entry|URL:http://raa.ruby-lang.org/project/depq/>))
|
27
27
|
|
data/lib/depq.rb
CHANGED
@@ -241,6 +241,11 @@ class Depq
|
|
241
241
|
super value, subpriority, priority
|
242
242
|
end
|
243
243
|
|
244
|
+
def initialize_in_queue(value, depq, index)
|
245
|
+
initialize(value, index, depq)
|
246
|
+
end
|
247
|
+
private :initialize_in_queue
|
248
|
+
|
244
249
|
def inspect
|
245
250
|
prio = self.priority
|
246
251
|
if self.value == prio
|
@@ -377,10 +382,8 @@ class Depq
|
|
377
382
|
|
378
383
|
def internal_inserted(depq, index)
|
379
384
|
raise ArgumentError, "already inserted" if in_queue?
|
380
|
-
priority = index_or_priority()
|
381
385
|
self.depq_or_subpriority = depq
|
382
386
|
self.index_or_priority = index
|
383
|
-
priority
|
384
387
|
end
|
385
388
|
private :internal_inserted
|
386
389
|
|
@@ -432,7 +435,6 @@ class Depq
|
|
432
435
|
@heapsize = 0
|
433
436
|
@mode = nil
|
434
437
|
@totalcount = 0
|
435
|
-
#@subpriority_generator = nil
|
436
438
|
end
|
437
439
|
|
438
440
|
# :stopdoc:
|
@@ -456,12 +458,10 @@ class Depq
|
|
456
458
|
end
|
457
459
|
private :set_entry
|
458
460
|
|
459
|
-
def
|
460
|
-
|
461
|
-
@ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = []
|
462
|
-
[locator, priority, subpriority]
|
461
|
+
def delete_last_entry
|
462
|
+
@ary.slice!(@ary.size-ARY_SLICE_SIZE, ARY_SLICE_SIZE)
|
463
463
|
end
|
464
|
-
private :
|
464
|
+
private :delete_last_entry
|
465
465
|
|
466
466
|
def each_entry
|
467
467
|
0.upto(self.size-1) {|i|
|
@@ -479,25 +479,37 @@ class Depq
|
|
479
479
|
private :mode_call
|
480
480
|
|
481
481
|
def use_min
|
482
|
-
|
482
|
+
case @mode
|
483
|
+
when :min, :interval
|
483
484
|
if @heapsize < self.size
|
484
485
|
@heapsize = mode_call(:heapify, @heapsize)
|
485
486
|
end
|
486
|
-
|
487
|
+
when :max
|
488
|
+
@mode = :interval
|
489
|
+
@heapsize = mode_call(:heapify)
|
490
|
+
when nil
|
487
491
|
@mode = :min
|
488
492
|
@heapsize = mode_call(:heapify)
|
493
|
+
else
|
494
|
+
raise "[bug] unexpected mode: #{@mode.inspect}"
|
489
495
|
end
|
490
496
|
end
|
491
497
|
private :use_min
|
492
498
|
|
493
499
|
def use_max
|
494
|
-
|
500
|
+
case @mode
|
501
|
+
when :max, :interval
|
495
502
|
if @heapsize < self.size
|
496
503
|
@heapsize = mode_call(:heapify, @heapsize)
|
497
504
|
end
|
498
|
-
|
505
|
+
when :min
|
506
|
+
@mode = :interval
|
507
|
+
@heapsize = mode_call(:heapify)
|
508
|
+
when nil
|
499
509
|
@mode = :max
|
500
510
|
@heapsize = mode_call(:heapify)
|
511
|
+
else
|
512
|
+
raise "[bug] unexpected mode: #{@mode.inspect}"
|
501
513
|
end
|
502
514
|
end
|
503
515
|
private :use_max
|
@@ -530,7 +542,6 @@ class Depq
|
|
530
542
|
private :check_locator
|
531
543
|
|
532
544
|
def default_subpriority
|
533
|
-
#return @subpriority_generator.call if @subpriority_generator
|
534
545
|
self.totalcount
|
535
546
|
end
|
536
547
|
private :default_subpriority
|
@@ -538,13 +549,14 @@ class Depq
|
|
538
549
|
def initialize_copy(obj) # :nodoc:
|
539
550
|
if defined? @ary
|
540
551
|
@ary = @ary.dup
|
541
|
-
|
542
|
-
|
552
|
+
n = @ary.length / ARY_SLICE_SIZE
|
553
|
+
k = 0
|
554
|
+
n.times {|i|
|
543
555
|
loc1 = @ary[k]
|
544
|
-
|
545
|
-
loc2
|
546
|
-
loc2.send(:internal_inserted, self, i)
|
556
|
+
loc2 = Depq::Locator.allocate
|
557
|
+
loc2.send(:initialize_in_queue, loc1.value, self, i)
|
547
558
|
@ary[k] = loc2
|
559
|
+
k += ARY_SLICE_SIZE
|
548
560
|
}
|
549
561
|
end
|
550
562
|
end
|
@@ -684,8 +696,9 @@ class Depq
|
|
684
696
|
|
685
697
|
def internal_set_priority(loc, priority, subpriority)
|
686
698
|
check_locator(loc)
|
687
|
-
|
688
|
-
|
699
|
+
index = loc.send(:index)
|
700
|
+
if @heapsize <= index
|
701
|
+
set_entry(index, loc, priority, subpriority)
|
689
702
|
else
|
690
703
|
mode_heapify
|
691
704
|
mode_call(:update_prio, loc, priority, subpriority)
|
@@ -705,9 +718,10 @@ class Depq
|
|
705
718
|
# p q.delete_min #=> 1
|
706
719
|
#
|
707
720
|
def insert_locator(loc)
|
721
|
+
priority = loc.priority
|
708
722
|
subpriority = loc.subpriority || default_subpriority
|
709
723
|
i = self.size
|
710
|
-
|
724
|
+
loc.send(:internal_inserted, self, i)
|
711
725
|
set_entry(i, loc, priority, subpriority)
|
712
726
|
@totalcount += 1
|
713
727
|
loc
|
@@ -961,7 +975,7 @@ class Depq
|
|
961
975
|
set_entry(index, loc2, priority2, subpriority2)
|
962
976
|
loc2.send(:index=, index)
|
963
977
|
end
|
964
|
-
|
978
|
+
delete_last_entry
|
965
979
|
loc.send(:internal_deleted, priority, subpriority)
|
966
980
|
loc
|
967
981
|
else
|
@@ -1010,8 +1024,7 @@ class Depq
|
|
1010
1024
|
#
|
1011
1025
|
def delete_min_priority
|
1012
1026
|
loc = delete_min_locator
|
1013
|
-
|
1014
|
-
[loc.value, loc.priority]
|
1027
|
+
loc and [loc.value, loc.priority]
|
1015
1028
|
end
|
1016
1029
|
|
1017
1030
|
# delete the minimum element in the queue and returns the value.
|
@@ -1075,8 +1088,7 @@ class Depq
|
|
1075
1088
|
#
|
1076
1089
|
def delete_max_priority
|
1077
1090
|
loc = delete_max_locator
|
1078
|
-
|
1079
|
-
[loc.value, loc.priority]
|
1091
|
+
loc and [loc.value, loc.priority]
|
1080
1092
|
end
|
1081
1093
|
|
1082
1094
|
# delete the maximum element in the queue and returns the value.
|
@@ -1138,8 +1150,7 @@ class Depq
|
|
1138
1150
|
#
|
1139
1151
|
def delete_unspecified_priority
|
1140
1152
|
loc = delete_unspecified_locator
|
1141
|
-
|
1142
|
-
[loc.value, loc.priority]
|
1153
|
+
loc and [loc.value, loc.priority]
|
1143
1154
|
end
|
1144
1155
|
|
1145
1156
|
# delete an element in the queue and returns the value.
|
@@ -1159,8 +1170,7 @@ class Depq
|
|
1159
1170
|
#
|
1160
1171
|
def delete_unspecified
|
1161
1172
|
loc = delete_unspecified_locator
|
1162
|
-
|
1163
|
-
loc.value
|
1173
|
+
loc and loc.value
|
1164
1174
|
end
|
1165
1175
|
|
1166
1176
|
# replaces the minimum element.
|
@@ -1233,7 +1243,7 @@ class Depq
|
|
1233
1243
|
# }
|
1234
1244
|
#
|
1235
1245
|
def each_locator # :yield: locator
|
1236
|
-
each_entry {|locator
|
1246
|
+
each_entry {|locator,|
|
1237
1247
|
yield locator
|
1238
1248
|
}
|
1239
1249
|
nil
|
@@ -1241,6 +1251,8 @@ class Depq
|
|
1241
1251
|
|
1242
1252
|
# iterate over the values and priorities in the queue.
|
1243
1253
|
#
|
1254
|
+
# The iteration order is unspecified.
|
1255
|
+
#
|
1244
1256
|
# q = Depq.new
|
1245
1257
|
# q.insert "durian", 1
|
1246
1258
|
# q.insert "banana", 3
|
@@ -1295,6 +1307,8 @@ class Depq
|
|
1295
1307
|
# p Depq.nlargest(3, [5, 2, 3, 1, 4, 6, 7]) {|e| -e } #=> [3, 2, 1]
|
1296
1308
|
#
|
1297
1309
|
def Depq.nlargest(n, iter)
|
1310
|
+
raise ArgumentError, "n is negative" if n < 0
|
1311
|
+
return [] if n == 0
|
1298
1312
|
limit = (n * Math.log(1+n)).ceil
|
1299
1313
|
limit = 1024 if limit < 1024
|
1300
1314
|
q = Depq.new
|
@@ -1348,6 +1362,8 @@ class Depq
|
|
1348
1362
|
# p Depq.nsmallest(5, [5, 2, 3, 1, 4, 6, 7]) {|e| -e } #=> [7, 6, 5, 4, 3]
|
1349
1363
|
#
|
1350
1364
|
def Depq.nsmallest(n, iter)
|
1365
|
+
raise ArgumentError, "n is negative" if n < 0
|
1366
|
+
return [] if n == 0
|
1351
1367
|
limit = (n * Math.log(1+n)).ceil
|
1352
1368
|
limit = 1024 if limit < 1024
|
1353
1369
|
q = Depq.new
|
@@ -1436,6 +1452,102 @@ class Depq
|
|
1436
1452
|
end
|
1437
1453
|
end
|
1438
1454
|
|
1455
|
+
# search a graph using A* search algorithm.
|
1456
|
+
#
|
1457
|
+
# The graph is defined by _start_ argument and the given block.
|
1458
|
+
# _start_ specifies the start node for searching.
|
1459
|
+
# The block should takes a node and return an array of pairs.
|
1460
|
+
# The pair is an 2-element array which contains the next node and cost of the given node to the next node.
|
1461
|
+
#
|
1462
|
+
# The optional argument, _heuristics_ specifies
|
1463
|
+
# conservative estimation to goal.
|
1464
|
+
# It should be a Hash or a Proc that _heuristics_+[node]+ returns an estimated cost to goal.
|
1465
|
+
# The estimated cost must be smaller or equal to the true cost.
|
1466
|
+
# If _heuristics_ is not given, Hash.new(0) is used.
|
1467
|
+
# This means +Depq.astar_search+ behaves as Dijkstra's algorithm in that case.
|
1468
|
+
#
|
1469
|
+
# +Depq.astar_search+ returns an enumerator.
|
1470
|
+
# It yields 3 values: previous node, current node and total cost between start node to current node.
|
1471
|
+
# When current node is start node, nil is given for previous node.
|
1472
|
+
#
|
1473
|
+
# # 7 5 1
|
1474
|
+
# # A--->B--->C--->D
|
1475
|
+
# # | | | |
|
1476
|
+
# # 2| 4| 1| 3|
|
1477
|
+
# # | | | |
|
1478
|
+
# # V V V V
|
1479
|
+
# # E--->F--->G--->H
|
1480
|
+
# # 3 3 5
|
1481
|
+
# #
|
1482
|
+
# g = {
|
1483
|
+
# :A => [[:B, 7], [:E, 2]],
|
1484
|
+
# :B => [[:C, 5], [:F, 4]],
|
1485
|
+
# :C => [[:D, 1], [:G, 1]],
|
1486
|
+
# :D => [[:H, 3]],
|
1487
|
+
# :E => [[:F, 3]],
|
1488
|
+
# :F => [[:G, 3]],
|
1489
|
+
# :G => [[:H, 5]],
|
1490
|
+
# :H => []
|
1491
|
+
# }
|
1492
|
+
# # This doesn't specify _heuristics_. So This is Dijkstra's algorithm.
|
1493
|
+
# Depq.astar_search(:A) {|n| g[n] }.each {|prev, curr, cost| p [prev, curr, cost] }
|
1494
|
+
# #=> [nil, :A, 0]
|
1495
|
+
# # [:A, :E, 2]
|
1496
|
+
# # [:E, :F, 5]
|
1497
|
+
# # [:A, :B, 7]
|
1498
|
+
# # [:F, :G, 8]
|
1499
|
+
# # [:B, :C, 12]
|
1500
|
+
# # [:G, :H, 13] # H found.
|
1501
|
+
# # [:C, :D, 13]
|
1502
|
+
#
|
1503
|
+
# # heuristics using Manhattan distance assuming the goal is H.
|
1504
|
+
# h = {
|
1505
|
+
# :A => 4,
|
1506
|
+
# :B => 3,
|
1507
|
+
# :C => 2,
|
1508
|
+
# :D => 1,
|
1509
|
+
# :E => 3,
|
1510
|
+
# :F => 2,
|
1511
|
+
# :G => 1,
|
1512
|
+
# :H => 0
|
1513
|
+
# }
|
1514
|
+
# # This specify _heuristics_. So This is A* search algorithm.
|
1515
|
+
# Depq.astar_search(:A, h) {|n| g[n] }.each {|prev, curr, cost| p [prev, curr, cost] }
|
1516
|
+
# #=> [nil, :A, 0]
|
1517
|
+
# # [:A, :E, 2]
|
1518
|
+
# # [:E, :F, 5]
|
1519
|
+
# # [:F, :G, 8]
|
1520
|
+
# # [:A, :B, 7]
|
1521
|
+
# # [:G, :H, 13] # H found. Bit better than Dijkstra's algorithm.
|
1522
|
+
# # [:B, :C, 12]
|
1523
|
+
# # [:C, :D, 13]
|
1524
|
+
#
|
1525
|
+
# cf. http://en.wikipedia.org/wiki/A*_search_algorithm
|
1526
|
+
#
|
1527
|
+
def Depq.astar_search(start, heuristics=nil, &find_nexts)
|
1528
|
+
Enumerator.new {|y|
|
1529
|
+
heuristics ||= Hash.new(0)
|
1530
|
+
h = Hash.new {|_, k| h[k] = heuristics[k] }
|
1531
|
+
q = Depq.new
|
1532
|
+
visited = {start => q.insert([nil, start], h[start])}
|
1533
|
+
until q.empty?
|
1534
|
+
path, w1 = q.delete_min_priority
|
1535
|
+
v1 = path.last
|
1536
|
+
w1 -= h[v1]
|
1537
|
+
y.yield [path.first, path.last, w1]
|
1538
|
+
find_nexts.call(v1).each {|v2, w2|
|
1539
|
+
w3 = w1 + w2 + h[v2]
|
1540
|
+
if !visited[v2]
|
1541
|
+
visited[v2] = q.insert([path.last,v2], w3)
|
1542
|
+
elsif w3 < visited[v2].priority
|
1543
|
+
visited[v2].update([path.last,v2], w3)
|
1544
|
+
end
|
1545
|
+
}
|
1546
|
+
end
|
1547
|
+
nil
|
1548
|
+
}
|
1549
|
+
end
|
1550
|
+
|
1439
1551
|
private
|
1440
1552
|
# :stopdoc:
|
1441
1553
|
|
@@ -1465,17 +1577,18 @@ class Depq
|
|
1465
1577
|
def mm_downheap(i, upper)
|
1466
1578
|
while true
|
1467
1579
|
j = i*2+1
|
1468
|
-
k =
|
1580
|
+
k = j+1
|
1469
1581
|
return if self.size <= j
|
1470
1582
|
if self.size == k
|
1471
1583
|
return if upper.call(i, j)
|
1472
1584
|
swap(i, j)
|
1473
1585
|
i = j
|
1586
|
+
return
|
1474
1587
|
else
|
1475
1588
|
return if upper.call(i, j) && upper.call(i, k)
|
1476
|
-
|
1477
|
-
swap(i,
|
1478
|
-
i =
|
1589
|
+
m = upper.call(j, k) ? j : k
|
1590
|
+
swap(i, m)
|
1591
|
+
i = m
|
1479
1592
|
end
|
1480
1593
|
end
|
1481
1594
|
end
|
@@ -1490,7 +1603,7 @@ class Depq
|
|
1490
1603
|
_, priority, subpriority = get_entry(i)
|
1491
1604
|
last = self.size - 1
|
1492
1605
|
loc.send(:internal_deleted, priority, subpriority)
|
1493
|
-
el, pl, sl =
|
1606
|
+
el, pl, sl = delete_last_entry
|
1494
1607
|
if i != last
|
1495
1608
|
set_entry(i, el, pl, sl)
|
1496
1609
|
el.send(:index=, i)
|
@@ -1642,10 +1755,10 @@ class Depq
|
|
1642
1755
|
def itv_maxside(i) i | 1 end
|
1643
1756
|
def itv_parent_minside(j) (j-2)/2 & ~1 end
|
1644
1757
|
def itv_parent_maxside(j) (j-2)/2 | 1 end
|
1645
|
-
def itv_child1_minside(i) i &= ~1;
|
1646
|
-
def itv_child1_maxside(i) i &= ~1;
|
1647
|
-
def itv_child2_minside(i) i &= ~1;
|
1648
|
-
def itv_child2_maxside(i) i &= ~1;
|
1758
|
+
def itv_child1_minside(i) i &= ~1; i*2+2 end
|
1759
|
+
def itv_child1_maxside(i) i &= ~1; i*2+3 end
|
1760
|
+
def itv_child2_minside(i) i &= ~1; i*2+4 end
|
1761
|
+
def itv_child2_maxside(i) i &= ~1; i*2+5 end
|
1649
1762
|
|
1650
1763
|
def pcmp(i, j)
|
1651
1764
|
ei, pi, si = get_entry(i)
|
@@ -1672,7 +1785,7 @@ class Depq
|
|
1672
1785
|
imin = itv_minside(i)
|
1673
1786
|
imax = itv_maxside(i)
|
1674
1787
|
if range.include?(imin) && range.include?(imax)
|
1675
|
-
if
|
1788
|
+
if scmp(imin, imax) > 0 && pcmp(imin, imax) == 0
|
1676
1789
|
swap imin, imax
|
1677
1790
|
end
|
1678
1791
|
end
|
@@ -1926,7 +2039,7 @@ class Depq
|
|
1926
2039
|
_, priority, subpriority = get_entry(i)
|
1927
2040
|
last = self.size - 1
|
1928
2041
|
loc.send(:internal_deleted, priority, subpriority)
|
1929
|
-
el, pl, sl =
|
2042
|
+
el, pl, sl = delete_last_entry
|
1930
2043
|
if i != last
|
1931
2044
|
set_entry(i, el, pl, sl)
|
1932
2045
|
el.send(:index=, i)
|
data/sample/dijkstra.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Dijkstra's single source shortest path finding algorithm
|
2
|
+
#
|
3
|
+
# usage:
|
4
|
+
# ruby -Ilib sample/dijkstra.rb
|
5
|
+
|
6
|
+
require 'depq'
|
7
|
+
|
8
|
+
def dijkstra_shortest_path(start, edges)
|
9
|
+
h = {}
|
10
|
+
edges.each {|v1, v2, w|
|
11
|
+
(h[v1] ||= []) << [v2, w]
|
12
|
+
}
|
13
|
+
h.default = []
|
14
|
+
q = Depq.new
|
15
|
+
visited = {start => q.insert([start], 0)}
|
16
|
+
until q.empty?
|
17
|
+
path, w1 = q.delete_min_priority
|
18
|
+
v1 = path.last
|
19
|
+
h[v1].each {|v2, w2|
|
20
|
+
if !visited[v2]
|
21
|
+
visited[v2] = q.insert(path+[v2], w1 + w2)
|
22
|
+
elsif w1 + w2 < visited[v2].priority
|
23
|
+
visited[v2].update(path+[v2], w1 + w2) # update val/prio
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
result = []
|
28
|
+
visited.each_value {|loc|
|
29
|
+
result << [loc.value, loc.priority]
|
30
|
+
}
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
E = [
|
35
|
+
['A', 'B', 2],
|
36
|
+
['A', 'C', 4],
|
37
|
+
['B', 'C', 1],
|
38
|
+
['C', 'B', 2],
|
39
|
+
['B', 'D', 3],
|
40
|
+
['C', 'D', 1],
|
41
|
+
]
|
42
|
+
p dijkstra_shortest_path('A', E)
|
43
|
+
|
data/sample/exqsort.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# external quick sort
|
2
|
+
#
|
3
|
+
# usage:
|
4
|
+
# ruby -Ilib sample/exqsort.rb input-file
|
5
|
+
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'depq'
|
8
|
+
|
9
|
+
NUM_PIVOTS = 1024
|
10
|
+
$tmpdir = nil
|
11
|
+
$filecount = 0
|
12
|
+
|
13
|
+
def partition(input)
|
14
|
+
q = Depq.new
|
15
|
+
fn1 = "#{$tmpdir}/#{$filecount += 1}"
|
16
|
+
fn2 = "#{$tmpdir}/#{$filecount += 1}"
|
17
|
+
fn3 = "#{$tmpdir}/#{$filecount += 1}"
|
18
|
+
open(fn1, "w") {|f1|
|
19
|
+
open(fn3, "w") {|f3|
|
20
|
+
n1 = n2 = n3 = 0
|
21
|
+
input.each_line {|line|
|
22
|
+
line = line.chomp
|
23
|
+
if q.size < NUM_PIVOTS
|
24
|
+
q.insert line
|
25
|
+
else
|
26
|
+
min, max = q.minmax
|
27
|
+
if line <= min
|
28
|
+
f1.puts line
|
29
|
+
n1 += 1
|
30
|
+
elsif line < max
|
31
|
+
q.insert line
|
32
|
+
if n1 < n2
|
33
|
+
f1.puts q.delete_min
|
34
|
+
n1 += 1
|
35
|
+
else
|
36
|
+
f3.puts q.delete_max
|
37
|
+
n3 += 1
|
38
|
+
end
|
39
|
+
else
|
40
|
+
f3.puts line
|
41
|
+
n3 += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
open(fn2, "w") {|f2|
|
48
|
+
while !q.empty?
|
49
|
+
f2.puts q.delete_min
|
50
|
+
end
|
51
|
+
}
|
52
|
+
if File.size(fn1) == 0
|
53
|
+
File.delete fn1
|
54
|
+
fn1 = nil
|
55
|
+
end
|
56
|
+
if File.size(fn2) == 0
|
57
|
+
File.delete fn2
|
58
|
+
fn2 = nil
|
59
|
+
end
|
60
|
+
if File.size(fn3) == 0
|
61
|
+
File.delete fn3
|
62
|
+
fn3 = nil
|
63
|
+
end
|
64
|
+
[fn1, fn2, fn3]
|
65
|
+
end
|
66
|
+
|
67
|
+
def eqs(input)
|
68
|
+
fn1, fn2, fn3 = partition(input)
|
69
|
+
r = []
|
70
|
+
if fn1
|
71
|
+
r.concat(open(fn1) {|f| eqs(f) })
|
72
|
+
end
|
73
|
+
if fn2
|
74
|
+
r << fn2
|
75
|
+
end
|
76
|
+
if fn3
|
77
|
+
r.concat(open(fn3) {|f| eqs(f) })
|
78
|
+
end
|
79
|
+
r
|
80
|
+
end
|
81
|
+
|
82
|
+
Dir.mktmpdir {|d|
|
83
|
+
$tmpdir = d
|
84
|
+
fns = eqs(ARGF)
|
85
|
+
fns.each {|fn|
|
86
|
+
File.foreach(fn) {|line|
|
87
|
+
puts line
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
data/sample/huffman.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Huffman coding
|
2
|
+
#
|
3
|
+
# usage:
|
4
|
+
# ruby -Ilib sample/huffman.rb input-file
|
5
|
+
|
6
|
+
require 'depq'
|
7
|
+
|
8
|
+
h = Hash.new(0)
|
9
|
+
ARGF.each {|line|
|
10
|
+
line.scan(/\S+/).each {|word|
|
11
|
+
h[word] += 1
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
if h.empty?
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
q = Depq.new
|
20
|
+
|
21
|
+
h.each {|word, count|
|
22
|
+
q.insert({word => ""}, count)
|
23
|
+
}
|
24
|
+
|
25
|
+
while 1 < q.size
|
26
|
+
h0, count0 = q.delete_min_priority
|
27
|
+
h1, count1 = q.delete_min_priority
|
28
|
+
hh = {}
|
29
|
+
h0.each {|w, c| hh[w] = "0" + c }
|
30
|
+
h1.each {|w, c| hh[w] = "1" + c }
|
31
|
+
q.insert hh, count0+count1
|
32
|
+
end
|
33
|
+
|
34
|
+
hh = q.delete_min
|
35
|
+
hh.keys.sort_by {|w| h[w] }.each {|w| puts "#{hh[w]} #{h[w]} #{w.inspect}" }
|
36
|
+
|
37
|
+
|
data/sample/int-3d.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# search integers x, y, z from smaller x*y*z where x >= y >= z >= 1.
|
2
|
+
#
|
3
|
+
# usage:
|
4
|
+
# ruby -Ilib sample/int-3d.rb
|
5
|
+
|
6
|
+
require 'depq'
|
7
|
+
|
8
|
+
# naive version
|
9
|
+
def gen1
|
10
|
+
q = Depq.new
|
11
|
+
q.insert [1, 1,1,1]
|
12
|
+
loop {
|
13
|
+
ary = q.delete_min
|
14
|
+
while q.find_min == ary
|
15
|
+
q.delete_min
|
16
|
+
end
|
17
|
+
yield ary
|
18
|
+
w, x,y,z = ary
|
19
|
+
x1 = x + 1
|
20
|
+
y1 = y + 1
|
21
|
+
z1 = z + 1
|
22
|
+
q.insert [x1*y*z, x1,y,z] if x1 >= y && y >= z
|
23
|
+
q.insert [x*y1*z, x,y1,z] if x >= y1 && y1 >= z
|
24
|
+
q.insert [x*y*z1, x,y,z1] if x >= y && y >= z1
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
# faster version
|
29
|
+
def gen2
|
30
|
+
q = Depq.new
|
31
|
+
q.insert [1, 1,1,1]
|
32
|
+
loop {
|
33
|
+
ary = q.delete_min
|
34
|
+
yield ary
|
35
|
+
w, x,y,z = ary
|
36
|
+
x1 = x + 1
|
37
|
+
y1 = y + 1
|
38
|
+
z1 = z + 1
|
39
|
+
q.insert [x1*y*z, x1,y,z]
|
40
|
+
q.insert [x*y1*z, x,y1,z] if x == y1
|
41
|
+
q.insert [x*y*z1, x,y,z1] if x == y && y == z1
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
gen2 {|w, x,y,z|
|
46
|
+
break if 100 <= w
|
47
|
+
p [w, x,y,z]
|
48
|
+
}
|
data/sample/kruskal.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Kruskal's algorithm
|
2
|
+
#
|
3
|
+
# finds a minimum spanning tree for a connected weighted graph
|
4
|
+
#
|
5
|
+
# http://en.wikipedia.org/wiki/Kruskal's_algorithm
|
6
|
+
#
|
7
|
+
# usage:
|
8
|
+
# ruby -Ilib sample/kruskal.rb
|
9
|
+
|
10
|
+
require 'depq'
|
11
|
+
|
12
|
+
def kruskal(edge_set)
|
13
|
+
q = Depq.new
|
14
|
+
parent = {}
|
15
|
+
edge_set.each {|v1, v2, w|
|
16
|
+
parent[v1] = nil
|
17
|
+
parent[v2] = nil
|
18
|
+
q.insert [v1, v2], w
|
19
|
+
}
|
20
|
+
|
21
|
+
edge_set2 = []
|
22
|
+
|
23
|
+
until q.empty?
|
24
|
+
v1, v2 = q.delete_min
|
25
|
+
r1 = v1
|
26
|
+
r1 = parent[r1] while parent[r1]
|
27
|
+
r2 = v2
|
28
|
+
r2 = parent[r2] while parent[r2]
|
29
|
+
if r1 != r2
|
30
|
+
edge_set2 << [v1, v2]
|
31
|
+
parent[r2] = r1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
edge_set2
|
36
|
+
end
|
37
|
+
|
38
|
+
if $0 == __FILE__
|
39
|
+
E = [
|
40
|
+
['A', 'B', 7],
|
41
|
+
['A', 'D', 5],
|
42
|
+
['B', 'C', 8],
|
43
|
+
['B', 'D', 9],
|
44
|
+
['B', 'E', 7],
|
45
|
+
['C', 'E', 5],
|
46
|
+
['D', 'E', 15],
|
47
|
+
['D', 'F', 6],
|
48
|
+
['E', 'F', 8],
|
49
|
+
['E', 'G', 9],
|
50
|
+
['F', 'G', 11],
|
51
|
+
]
|
52
|
+
|
53
|
+
p kruskal(E)
|
54
|
+
#=> [["A", "D"], ["C", "E"], ["D", "F"], ["A", "B"], ["B", "E"], ["E", "G"]]
|
55
|
+
end
|
data/sample/maze.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# solve a maze using A* search algorithm
|
2
|
+
#
|
3
|
+
# usage:
|
4
|
+
# ruby -Isample -Ilib sample/maze.rb
|
5
|
+
|
6
|
+
require 'depq'
|
7
|
+
|
8
|
+
Pos = Struct.new(:x, :y)
|
9
|
+
|
10
|
+
MAZE_MAP = <<'End'
|
11
|
+
OOOOOOOOOOOOOOOOOOOOOOOOO
|
12
|
+
O OSO O O
|
13
|
+
O OOOOOOOOO O O OOO OOO O
|
14
|
+
O O O O O O
|
15
|
+
O OOOOOOO OOOOOOO OOO O O
|
16
|
+
O O O O O O
|
17
|
+
OOOOO O O OOO OOOOOOOOO O
|
18
|
+
OG O O O O O O O
|
19
|
+
O O O O OOO OOO O O O OOO
|
20
|
+
O O O O O O O O
|
21
|
+
O OOO O OOOOOOOOOOOOOOO O
|
22
|
+
O O O
|
23
|
+
OOOOOOOOOOOOOOOOOOOOOOOOO
|
24
|
+
End
|
25
|
+
|
26
|
+
MAZE = MAZE_MAP.lines.map {|line| line.chomp.split(//) }
|
27
|
+
|
28
|
+
H = HEIGHT = MAZE.length
|
29
|
+
W = WIDTH = MAZE[0].length
|
30
|
+
|
31
|
+
MAZE.each_with_index {|line,y|
|
32
|
+
line.each_with_index {|cell,x|
|
33
|
+
if cell == 'S'
|
34
|
+
START = Pos[x,y]
|
35
|
+
MAZE[y][x] = ' '
|
36
|
+
elsif cell == 'G'
|
37
|
+
GOAL = Pos[x,y]
|
38
|
+
MAZE[y][x] = ' '
|
39
|
+
end
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
find_nexts4 = proc {|pos|
|
44
|
+
x, y = pos.x, pos.y
|
45
|
+
r = []
|
46
|
+
r << [Pos[x-1,y],1] if 0 < x && MAZE[y][x-1] == ' '
|
47
|
+
r << [Pos[x,y-1],1] if 0 < y && MAZE[y-1][x] == ' '
|
48
|
+
r << [Pos[x+1,y],1] if x < W-1 && MAZE[y][x+1] == ' '
|
49
|
+
r << [Pos[x,y+1],1] if y < H-1 && MAZE[y+1][x] == ' '
|
50
|
+
r
|
51
|
+
}
|
52
|
+
|
53
|
+
heuristics4 = proc {|pos|
|
54
|
+
x, y = pos.x, pos.y
|
55
|
+
(x-GOAL.x).abs + (y-GOAL.y).abs
|
56
|
+
}
|
57
|
+
|
58
|
+
find_nexts8 = proc {|pos|
|
59
|
+
x, y = pos.x, pos.y
|
60
|
+
r = []
|
61
|
+
r << [[x-1,y],1] if 0 < x && MAZE[y][x-1] == ' '
|
62
|
+
r << [[x,y-1],1] if 0 < y && MAZE[y-1][x] == ' '
|
63
|
+
r << [[x+1,y],1] if x < W-1 && MAZE[y][x+1] == ' '
|
64
|
+
r << [[x,y+1],1] if y < H-1 && MAZE[y+1][x] == ' '
|
65
|
+
r << [[x-1,y-1],2] if 0 < x && 0 < y && MAZE[y-1][x-1] == ' '
|
66
|
+
r << [[x-1,y+1],2] if 0 < x && y < H-1 && MAZE[y+1][x-1] == ' '
|
67
|
+
r << [[x+1,y-1],2] if x < W-1 && 0 < y && MAZE[y-1][x+1] == ' '
|
68
|
+
r << [[x+1,y+1],2] if x < W-1 && y < H-1 && MAZE[y+1][x+1] == ' '
|
69
|
+
r
|
70
|
+
}
|
71
|
+
|
72
|
+
heuristics8 = proc {|pos|
|
73
|
+
x, y = pos.x, pos.y
|
74
|
+
(x-GOAL.x).abs + (y-GOAL.y).abs
|
75
|
+
}
|
76
|
+
|
77
|
+
t1 = Time.now
|
78
|
+
searched = {}
|
79
|
+
path = Depq.astar_search(START, heuristics4, &find_nexts4).each {|prev, cur, w|
|
80
|
+
searched[cur] = [searched[prev], cur]
|
81
|
+
if cur == GOAL
|
82
|
+
break searched[cur].flatten.compact
|
83
|
+
end
|
84
|
+
}
|
85
|
+
t2 = Time.now
|
86
|
+
p t2-t1
|
87
|
+
|
88
|
+
searched.each_key {|pos|
|
89
|
+
x, y = pos.x, pos.y
|
90
|
+
MAZE[y][x] = '.'
|
91
|
+
}
|
92
|
+
|
93
|
+
path.each {|pos|
|
94
|
+
x, y = pos.x, pos.y
|
95
|
+
MAZE[y][x] = '*'
|
96
|
+
}
|
97
|
+
|
98
|
+
MAZE[START.y][START.x] = 'S'
|
99
|
+
MAZE[GOAL.y][GOAL.x] = 'G'
|
100
|
+
|
101
|
+
MAZE.each {|line|
|
102
|
+
puts line.join('')
|
103
|
+
}
|
data/sample/prim.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# Prim's algorithm
|
2
|
+
#
|
3
|
+
# finds a minimum spanning tree for a connected weighted graph
|
4
|
+
#
|
5
|
+
# http://en.wikipedia.org/wiki/Prim's_algorithm
|
6
|
+
#
|
7
|
+
# usage:
|
8
|
+
# ruby -Ilib sample/prim.rb
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'depq'
|
12
|
+
|
13
|
+
def prim(edge_set)
|
14
|
+
vertex_set1 = {}
|
15
|
+
adj = {}
|
16
|
+
weight = {}
|
17
|
+
edge_set.each {|v1, v2, w|
|
18
|
+
vertex_set1[v1] = true
|
19
|
+
vertex_set1[v2] = true
|
20
|
+
adj[v1] ||= []
|
21
|
+
adj[v1] << v2
|
22
|
+
adj[v2] ||= []
|
23
|
+
adj[v2] << v1
|
24
|
+
weight[[v1, v2]] = weight[[v2, v1]] = w
|
25
|
+
}
|
26
|
+
adj.default = []
|
27
|
+
|
28
|
+
start = vertex_set1.first[0]
|
29
|
+
q = Depq.new
|
30
|
+
vertex_set2 = {start => true}
|
31
|
+
edge_set2 = []
|
32
|
+
prev_locators = {}
|
33
|
+
|
34
|
+
adj[start].each {|v2|
|
35
|
+
prev_locators[v2] = [start, q.insert(v2, weight[[start, v2]])]
|
36
|
+
}
|
37
|
+
|
38
|
+
while vertex_set2.size < vertex_set1.size
|
39
|
+
v2 = q.delete_min
|
40
|
+
v1, _ = prev_locators[v2]
|
41
|
+
prev_locators.delete v2
|
42
|
+
vertex_set2[v2] = true
|
43
|
+
edge_set2 << [v1, v2]
|
44
|
+
adj[v2].each {|v3|
|
45
|
+
next if vertex_set2[v3]
|
46
|
+
if prev_loc = prev_locators[v3]
|
47
|
+
_, loc = prev_loc
|
48
|
+
if weight[[v2, v3]] < loc.priority
|
49
|
+
prev_loc[0] = v2
|
50
|
+
loc.update v3, weight[[v2, v3]]
|
51
|
+
end
|
52
|
+
else
|
53
|
+
prev_locators[v3] = [v2, q.insert(v3, weight[[v2, v3]])]
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
edge_set2
|
58
|
+
end
|
59
|
+
|
60
|
+
if $0 == __FILE__
|
61
|
+
E = [
|
62
|
+
['A', 'B', 7],
|
63
|
+
['A', 'D', 5],
|
64
|
+
['B', 'C', 8],
|
65
|
+
['B', 'D', 9],
|
66
|
+
['B', 'E', 7],
|
67
|
+
['C', 'E', 5],
|
68
|
+
['D', 'E', 15],
|
69
|
+
['D', 'F', 6],
|
70
|
+
['E', 'F', 8],
|
71
|
+
['E', 'G', 9],
|
72
|
+
['F', 'G', 11],
|
73
|
+
]
|
74
|
+
|
75
|
+
p prim(E)
|
76
|
+
#=> [["A", "D"], ["D", "F"], ["A", "B"], ["B", "E"], ["E", "C"], ["E", "G"]]
|
77
|
+
end
|
78
|
+
|
data/sample/sched.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# scheduler
|
2
|
+
#
|
3
|
+
# needs Ruby 1.9.2 for timeout argument of ConditionVariable#wait.
|
4
|
+
#
|
5
|
+
# usage:
|
6
|
+
# ruby -Ilib sample/sched.rb
|
7
|
+
|
8
|
+
require 'depq'
|
9
|
+
require 'thread'
|
10
|
+
|
11
|
+
class Sched
|
12
|
+
def initialize
|
13
|
+
@q = Depq.new
|
14
|
+
@m = Mutex.new
|
15
|
+
@cv = ConditionVariable.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def sync
|
19
|
+
@m.synchronize { yield }
|
20
|
+
end
|
21
|
+
|
22
|
+
def insert(time, &block)
|
23
|
+
sync {
|
24
|
+
@q.insert block, time
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def wait_next_event
|
29
|
+
sync {
|
30
|
+
block, time = @q.find_min_priority
|
31
|
+
now = Time.now
|
32
|
+
if now < time
|
33
|
+
@cv.wait(@m, time - now)
|
34
|
+
end
|
35
|
+
now
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
until sync { @q.empty? }
|
41
|
+
now = wait_next_event
|
42
|
+
loop {
|
43
|
+
block = time = nil
|
44
|
+
sync {
|
45
|
+
raise StopIteration if @q.empty?
|
46
|
+
block, time = @q.find_min_priority
|
47
|
+
raise StopIteration if now < time
|
48
|
+
block, time = @q.delete_min_priority
|
49
|
+
}
|
50
|
+
block.call
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if $0 == __FILE__
|
57
|
+
now = Time.now
|
58
|
+
sc = Sched.new
|
59
|
+
Thread.new {
|
60
|
+
sleep 1.5
|
61
|
+
sc.insert(Time.now+1) { p Time.now-now }
|
62
|
+
}
|
63
|
+
sc.insert(now+4) { p Time.now-now }
|
64
|
+
sc.insert(now+3) { p Time.now-now }
|
65
|
+
sc.insert(now+2) { p Time.now-now }
|
66
|
+
sc.insert(now+1) { p Time.now-now }
|
67
|
+
sc.start
|
68
|
+
end
|
data/test/test-depq.rb
CHANGED
@@ -1097,4 +1097,75 @@ class TestDepq < Test::Unit::TestCase
|
|
1097
1097
|
assert_raise(StopIteration) { e.next }
|
1098
1098
|
end
|
1099
1099
|
|
1100
|
+
def test_mode_min_first
|
1101
|
+
q = Depq.new
|
1102
|
+
q.insert 1
|
1103
|
+
q.insert 2
|
1104
|
+
assert_equal(nil, q.instance_eval { @mode })
|
1105
|
+
assert_equal(1, q.delete_min)
|
1106
|
+
assert_equal(:min, q.instance_eval { @mode })
|
1107
|
+
assert_equal(2, q.delete_max)
|
1108
|
+
assert_equal(:interval, q.instance_eval { @mode })
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
def test_mode_max_first
|
1112
|
+
q = Depq.new
|
1113
|
+
q.insert 1
|
1114
|
+
q.insert 2
|
1115
|
+
assert_equal(nil, q.instance_eval { @mode })
|
1116
|
+
assert_equal(2, q.delete_max)
|
1117
|
+
assert_equal(:max, q.instance_eval { @mode })
|
1118
|
+
assert_equal(1, q.delete_min)
|
1119
|
+
assert_equal(:interval, q.instance_eval { @mode })
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
def test_astar
|
1123
|
+
g = {
|
1124
|
+
:A => [[:B, 7], [:E, 2]],
|
1125
|
+
:B => [[:C, 5], [:F, 4]],
|
1126
|
+
:C => [[:D, 1], [:G, 1]],
|
1127
|
+
:D => [[:H, 3]],
|
1128
|
+
:E => [[:F, 3]],
|
1129
|
+
:F => [[:G, 3]],
|
1130
|
+
:G => [[:H, 5]],
|
1131
|
+
:H => []
|
1132
|
+
}
|
1133
|
+
res = []
|
1134
|
+
Depq.astar_search(:A) {|n| g[n] }.each {|prev, curr, cost| res << [prev, curr, cost] }
|
1135
|
+
assert_equal(
|
1136
|
+
[[nil, :A, 0],
|
1137
|
+
[:A, :E, 2],
|
1138
|
+
[:E, :F, 5],
|
1139
|
+
[:A, :B, 7],
|
1140
|
+
[:F, :G, 8],
|
1141
|
+
[:B, :C, 12],
|
1142
|
+
[:G, :H, 13],
|
1143
|
+
[:C, :D, 13]],
|
1144
|
+
res)
|
1145
|
+
|
1146
|
+
# heuristics using Manhattan distance assuming the goal is H.
|
1147
|
+
h = {
|
1148
|
+
:A => 4,
|
1149
|
+
:B => 3,
|
1150
|
+
:C => 2,
|
1151
|
+
:D => 1,
|
1152
|
+
:E => 3,
|
1153
|
+
:F => 2,
|
1154
|
+
:G => 1,
|
1155
|
+
:H => 0
|
1156
|
+
}
|
1157
|
+
res = []
|
1158
|
+
Depq.astar_search(:A, h) {|n| g[n] }.each {|prev, curr, cost| res << [prev, curr, cost] }
|
1159
|
+
assert_equal(
|
1160
|
+
[[nil, :A, 0],
|
1161
|
+
[:A, :E, 2],
|
1162
|
+
[:E, :F, 5],
|
1163
|
+
[:F, :G, 8],
|
1164
|
+
[:A, :B, 7],
|
1165
|
+
[:G, :H, 13],
|
1166
|
+
[:B, :C, 12],
|
1167
|
+
[:C, :D, 13]],
|
1168
|
+
res)
|
1169
|
+
end
|
1170
|
+
|
1100
1171
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: depq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.6'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'depq is a Double-Ended Priority Queue library.
|
15
15
|
|
@@ -28,6 +28,14 @@ extra_rdoc_files: []
|
|
28
28
|
files:
|
29
29
|
- README
|
30
30
|
- lib/depq.rb
|
31
|
+
- sample/dijkstra.rb
|
32
|
+
- sample/exqsort.rb
|
33
|
+
- sample/huffman.rb
|
34
|
+
- sample/int-3d.rb
|
35
|
+
- sample/kruskal.rb
|
36
|
+
- sample/maze.rb
|
37
|
+
- sample/prim.rb
|
38
|
+
- sample/sched.rb
|
31
39
|
- test/test-depq.rb
|
32
40
|
homepage: http://depq.rubyforge.org/
|
33
41
|
licenses: []
|