depq 0.5 → 0.6
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/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: []
|