depq 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README +6 -9
  2. data/depq.rb +363 -60
  3. data/test-depq.rb +46 -0
  4. metadata +4 -4
data/README CHANGED
@@ -139,15 +139,12 @@ A* search algorithm, etc.
139
139
  # [["A", "B", "C"], 3],
140
140
  # [["A", "B", "C", "D"], 4]]
141
141
 
142
- = Internal Heap Algorithm and Performance Tips
143
-
144
- Depq uses min-heap or max-heap internally.
145
- When delete_min is used, min-heap is constructed and max-heap is destructed.
146
- When delete_max is used, max-heap is constructed and min-heap is destructed.
147
- So mixing delete_min and delete_max causes bad performance.
148
- In future, min-max-heap may be implemented to avoid this problem.
149
- min-max-heap will be used when delete_min and delete_max is used both.
150
- (Because min-max-heap is slower than min-heap/max-heap.)
142
+ = Internal Heap Algorithm
143
+
144
+ Depq uses min-heap, max-heap or interval-heap internally.
145
+ When delete_min is used, min-heap is constructed.
146
+ When delete_max is used, max-heap is constructed.
147
+ When delete_min and delete_max is used, interval-heap is constructed.
151
148
 
152
149
  = Author
153
150
 
data/depq.rb CHANGED
@@ -165,15 +165,12 @@
165
165
  # # [["A", "B", "C"], 3],
166
166
  # # [["A", "B", "C", "D"], 4]]
167
167
  #
168
- # = Internal Heap Algorithm and Performance Tips
168
+ # = Internal Heap Algorithm
169
169
  #
170
- # Depq uses min-heap or max-heap internally.
171
- # When delete_min is used, min-heap is constructed and max-heap is destructed.
172
- # When delete_max is used, max-heap is constructed and min-heap is destructed.
173
- # So mixing delete_min and delete_max causes bad performance.
174
- # In future, min-max-heap may be implemented to avoid this problem.
175
- # min-max-heap will be used when delete_min and delete_max is used both.
176
- # (Because min-max-heap is slower than min-heap/max-heap.)
170
+ # Depq uses min-heap, max-heap or interval-heap internally.
171
+ # When delete_min is used, min-heap is constructed.
172
+ # When delete_max is used, max-heap is constructed.
173
+ # When delete_min and delete_max is used, interval-heap is constructed.
177
174
  #
178
175
  class Depq
179
176
  include Enumerable
@@ -217,8 +214,6 @@ class Depq
217
214
  define_method(:eql?, Object.instance_method(:eql?))
218
215
  define_method(:hash, Object.instance_method(:hash))
219
216
 
220
- include Comparable
221
-
222
217
  # Create a Depq::Locator object.
223
218
  def initialize(value, priority=value, subpriority=nil)
224
219
  super value, subpriority, priority
@@ -447,25 +442,41 @@ class Depq
447
442
  end
448
443
  private :each_entry
449
444
 
450
- def min_mode
451
- if @mode != MinHeap
445
+ def use_min
446
+ if @mode == MinHeap || @mode == IntervalHeap
447
+ if @heapsize < self.size
448
+ @heapsize = @mode.heapify(self, @ary, @heapsize)
449
+ end
450
+ else
452
451
  @mode = MinHeap
453
452
  @heapsize = @mode.heapify(self, @ary)
454
- elsif @heapsize < self.size
455
- @heapsize = @mode.heapify(self, @ary, @heapsize)
456
453
  end
457
454
  end
458
- private :min_mode
455
+ private :use_min
459
456
 
460
- def max_mode
461
- if @mode != MaxHeap
457
+ def use_max
458
+ if @mode == MaxHeap || @mode == IntervalHeap
459
+ if @heapsize < self.size
460
+ @heapsize = @mode.heapify(self, @ary, @heapsize)
461
+ end
462
+ else
462
463
  @mode = MaxHeap
463
464
  @heapsize = @mode.heapify(self, @ary)
464
- elsif @heapsize < self.size
465
- @heapsize = @mode.heapify(self, @ary, @heapsize)
466
465
  end
467
466
  end
468
- private :max_mode
467
+ private :use_max
468
+
469
+ def use_minmax
470
+ if @mode == IntervalHeap
471
+ if @heapsize < self.size
472
+ @heapsize = @mode.heapify(self, @ary, @heapsize)
473
+ end
474
+ else
475
+ @mode = IntervalHeap
476
+ @heapsize = @mode.heapify(self, @ary)
477
+ end
478
+ end
479
+ private :use_minmax
469
480
 
470
481
  def mode_heapify
471
482
  if @mode
@@ -756,8 +767,8 @@ class Depq
756
767
  #
757
768
  def find_min_locator
758
769
  return nil if empty?
759
- min_mode
760
- @mode.find_min_locator(@ary)
770
+ use_min
771
+ @mode.find_min_locator(self, @ary)
761
772
  end
762
773
 
763
774
  # return the minimum value with its priority.
@@ -810,8 +821,8 @@ class Depq
810
821
  #
811
822
  def find_max_locator
812
823
  return nil if empty?
813
- max_mode
814
- @mode.find_max_locator(@ary)
824
+ use_max
825
+ @mode.find_max_locator(self, @ary)
815
826
  end
816
827
 
817
828
  # return the maximum value with its priority.
@@ -862,35 +873,8 @@ class Depq
862
873
  #
863
874
  def find_minmax_locator
864
875
  return [nil, nil] if empty?
865
- case @mode
866
- when :min
867
- loc1 = find_min_locator
868
- loc2 = loc1
869
- self.each_locator {|loc|
870
- if compare_for_max(loc2.priority, loc2.subpriority, loc.priority, loc.subpriority) < 0
871
- loc2 = loc
872
- end
873
- }
874
- when :max
875
- loc2 = find_max_locator
876
- loc1 = loc2
877
- self.each_locator {|loc|
878
- if compare_for_min(loc1.priority, loc1.subpriority, loc.priority, loc.subpriority) > 0
879
- loc1 = loc
880
- end
881
- }
882
- else
883
- loc1 = loc2 = nil
884
- self.each_locator {|loc|
885
- if loc1 == nil || compare_for_min(loc1.priority, loc1.subpriority, loc.priority, loc.subpriority) > 0
886
- loc1 = loc
887
- end
888
- if loc2 == nil || compare_for_max(loc2.priority, loc2.subpriority, loc.priority, loc.subpriority) < 0
889
- loc2 = loc
890
- end
891
- }
892
- end
893
- [loc1, loc2]
876
+ use_minmax
877
+ return @mode.find_minmax_locator(self, @ary)
894
878
  end
895
879
 
896
880
  # returns the minimum and maximum value as a two-element array.
@@ -953,8 +937,8 @@ class Depq
953
937
  #
954
938
  def delete_min_locator
955
939
  return nil if empty?
956
- min_mode
957
- loc = @mode.find_min_locator(@ary)
940
+ use_min
941
+ loc = @mode.find_min_locator(self, @ary)
958
942
  @heapsize = @mode.delete_locator(self, @ary, loc)
959
943
  loc
960
944
  end
@@ -1018,8 +1002,8 @@ class Depq
1018
1002
  #
1019
1003
  def delete_max_locator
1020
1004
  return nil if empty?
1021
- max_mode
1022
- loc = @mode.find_max_locator(@ary)
1005
+ use_max
1006
+ loc = @mode.find_max_locator(self, @ary)
1023
1007
  @heapsize = @mode.delete_locator(self, @ary, loc)
1024
1008
  loc
1025
1009
  end
@@ -1320,8 +1304,7 @@ class Depq
1320
1304
 
1321
1305
  # :stopdoc:
1322
1306
 
1323
- module SimpleHeap
1324
-
1307
+ module HeapArray
1325
1308
  def size(ary)
1326
1309
  return ary.size / ARY_SLICE_SIZE
1327
1310
  end
@@ -1364,6 +1347,10 @@ class Depq
1364
1347
  ei.send(:index=, j)
1365
1348
  ej.send(:index=, i)
1366
1349
  end
1350
+ end
1351
+
1352
+ module SimpleHeap
1353
+ include HeapArray
1367
1354
 
1368
1355
  def upheap(pd, ary, j)
1369
1356
  while true
@@ -1393,7 +1380,7 @@ class Depq
1393
1380
  end
1394
1381
  end
1395
1382
 
1396
- def find_top_locator(ary)
1383
+ def find_top_locator(pd, ary)
1397
1384
  loc, _ = get_entry(ary, 0)
1398
1385
  loc
1399
1386
  end
@@ -1516,5 +1503,321 @@ class Depq
1516
1503
  alias find_max_locator find_top_locator
1517
1504
  end
1518
1505
 
1506
+ module IntervalHeap
1507
+ end
1508
+ class << IntervalHeap
1509
+ include HeapArray
1510
+
1511
+ def root?(i) i < 2 end
1512
+ def minside?(i) i.even? end
1513
+ def maxside?(i) i.odd? end
1514
+ def minside(i) i & ~1 end
1515
+ def maxside(i) i | 1 end
1516
+ def parent_minside(j) (j-2)/2 & ~1 end
1517
+ def parent_maxside(j) (j-2)/2 | 1 end
1518
+ def child1_minside(i) i &= ~1; (i*2+2) & ~1 end
1519
+ def child1_maxside(i) i &= ~1; (i*2+2) | 1 end
1520
+ def child2_minside(i) i &= ~1; (i*2+4) & ~1 end
1521
+ def child2_maxside(i) i &= ~1; (i*2+4) | 1 end
1522
+
1523
+ def pcmp(pd, ary, i, j)
1524
+ ei, pi, si = get_entry(ary, i)
1525
+ ej, pj, sj = get_entry(ary, j)
1526
+ pd.compare_priority(pi, pj)
1527
+ end
1528
+
1529
+ def scmp(pd, ary, i, j)
1530
+ ei, pi, si = get_entry(ary, i)
1531
+ ej, pj, sj = get_entry(ary, j)
1532
+ si <=> sj
1533
+ end
1534
+
1535
+ def psame(pd, ary, i)
1536
+ pcmp(pd, ary, minside(i), maxside(i)) == 0
1537
+ end
1538
+
1539
+ def travel(pd, ary, i, range, fix_subpriority)
1540
+ while true
1541
+ j = yield i
1542
+ return i if !j
1543
+ swap ary, i, j
1544
+ if fix_subpriority
1545
+ imin = minside(i)
1546
+ imax = maxside(i)
1547
+ if range.include?(imin) && range.include?(imax)
1548
+ if pcmp(pd, ary, imin, imax) == 0 && scmp(pd, ary, imin, imax) > 0
1549
+ swap ary, imin, imax
1550
+ end
1551
+ end
1552
+ end
1553
+ i = j
1554
+ end
1555
+ end
1556
+
1557
+ def upheap_minside(pd, ary, i, range)
1558
+ travel(pd, ary, i, range, true) {|j|
1559
+ if root?(j)
1560
+ nil
1561
+ elsif !range.include?(k = parent_minside(j))
1562
+ nil
1563
+ else
1564
+ if pcmp(pd, ary, k, j) > 0
1565
+ swap(ary, minside(k), maxside(k)) if psame(pd, ary, k)
1566
+ k
1567
+ else
1568
+ nil
1569
+ end
1570
+ end
1571
+ }
1572
+ end
1573
+
1574
+ def upheap_maxside(pd, ary, i, range)
1575
+ travel(pd, ary, i, range, true) {|j|
1576
+ if root?(j)
1577
+ nil
1578
+ elsif !range.include?(k = parent_maxside(j))
1579
+ nil
1580
+ else
1581
+ if pcmp(pd, ary, k, j) < 0
1582
+ k
1583
+ else
1584
+ nil
1585
+ end
1586
+ end
1587
+ }
1588
+ end
1589
+
1590
+ def downheap_minside(pd, ary, i, range)
1591
+ travel(pd, ary, i, range, true) {|j|
1592
+ k1 = child1_minside(j)
1593
+ k2 = child2_minside(j)
1594
+ if !range.include?(k1)
1595
+ nil
1596
+ else
1597
+ if !range.include?(k2)
1598
+ k = k1
1599
+ else
1600
+ if (pc = pcmp(pd, ary, k1, k2)) < 0
1601
+ k = k1
1602
+ elsif pc > 0
1603
+ k = k2
1604
+ elsif (sc = scmp(pd, ary, k1, k2)) <= 0
1605
+ k = k1
1606
+ else
1607
+ k = k2
1608
+ end
1609
+ end
1610
+ if (pc = pcmp(pd, ary, k, j)) < 0
1611
+ k
1612
+ else
1613
+ nil
1614
+ end
1615
+ end
1616
+ }
1617
+ end
1618
+
1619
+ def downheap_maxside(pd, ary, i, range)
1620
+ travel(pd, ary, i, range, true) {|j|
1621
+ k1 = child1_maxside(j)
1622
+ k2 = child2_maxside(j)
1623
+ k1 = minside(k1) if range.include?(k1) && psame(pd, ary, k1)
1624
+ k2 = minside(k2) if range.include?(k2) && psame(pd, ary, k2)
1625
+ if !range.include?(k1)
1626
+ nil
1627
+ else
1628
+ if !range.include?(k2)
1629
+ k = k1
1630
+ else
1631
+ if (pc = pcmp(pd, ary, k1, k2)) < 0
1632
+ k = k2
1633
+ elsif pc > 0
1634
+ k = k1
1635
+ elsif (sc = scmp(pd, ary, k1, k2)) <= 0
1636
+ k = k1
1637
+ else
1638
+ k = k2
1639
+ end
1640
+ end
1641
+ if (pc = pcmp(pd, ary, k, j)) > 0
1642
+ swap(ary, minside(k), maxside(k)) if minside?(k)
1643
+ maxside(k)
1644
+ else
1645
+ nil
1646
+ end
1647
+ end
1648
+ }
1649
+ end
1650
+
1651
+ def upheap_sub(pd, ary, i, range)
1652
+ travel(pd, ary, i, range, false) {|j|
1653
+ k = nil
1654
+ if minside?(j)
1655
+ if range.include?(kk=parent_maxside(j)) && pcmp(pd, ary, j, kk) == 0
1656
+ k = kk
1657
+ elsif range.include?(kk=parent_minside(j)) && pcmp(pd, ary, j, kk) == 0
1658
+ k = kk
1659
+ end
1660
+ else
1661
+ if range.include?(kk=minside(j)) && pcmp(pd, ary, j, kk) == 0
1662
+ k = kk
1663
+ elsif range.include?(kk=parent_maxside(j)) && pcmp(pd, ary, j, kk) == 0
1664
+ k = kk
1665
+ end
1666
+ end
1667
+ if !k
1668
+ nil
1669
+ elsif !range.include?(k)
1670
+ nil
1671
+ elsif scmp(pd, ary, k, j) > 0
1672
+ k
1673
+ else
1674
+ nil
1675
+ end
1676
+ }
1677
+ end
1678
+
1679
+ def downheap_sub(pd, ary, i, range)
1680
+ travel(pd, ary, i, range, false) {|j|
1681
+ k1 = k2 = nil
1682
+ if minside?(j)
1683
+ if range.include?(kk=maxside(j)) && pcmp(pd, ary, j, kk) == 0
1684
+ k1 = kk
1685
+ else
1686
+ k1 = kk if range.include?(kk=child1_minside(j)) && pcmp(pd, ary, j, kk) == 0
1687
+ k2 = kk if range.include?(kk=child2_minside(j)) && pcmp(pd, ary, j, kk) == 0
1688
+ end
1689
+ else
1690
+ if range.include?(kk=child1_minside(j)) && pcmp(pd, ary, j, kk) == 0
1691
+ k1 = kk
1692
+ elsif range.include?(kk=child1_maxside(j)) && pcmp(pd, ary, j, kk) == 0
1693
+ k1 = kk
1694
+ end
1695
+ if range.include?(kk=child2_minside(j)) && pcmp(pd, ary, j, kk) == 0
1696
+ k2 = kk
1697
+ elsif range.include?(kk=child2_maxside(j)) && pcmp(pd, ary, j, kk) == 0
1698
+ k2 = kk
1699
+ end
1700
+ end
1701
+ if k1 && k2
1702
+ k = scmp(pd, ary, k1, k2) > 0 ? k2 : k1
1703
+ else
1704
+ k = k1 || k2
1705
+ end
1706
+ if k && scmp(pd, ary, k, j) < 0
1707
+ k
1708
+ else
1709
+ nil
1710
+ end
1711
+ }
1712
+ end
1713
+
1714
+ def adjust(pd, ary, i, range)
1715
+ if minside?(i)
1716
+ j = upheap_minside(pd, ary, i, range)
1717
+ if i == j
1718
+ i = downheap_minside(pd, ary, i, range)
1719
+ if !range.include?(child1_minside(i)) && range.include?(j=maxside(i)) && pcmp(pd, ary, i, j) > 0
1720
+ swap(ary, i, j)
1721
+ i = j
1722
+ end
1723
+ if maxside?(i) || !range.include?(maxside(i))
1724
+ i = upheap_maxside(pd, ary, i, range)
1725
+ end
1726
+ end
1727
+ else
1728
+ j = upheap_maxside(pd, ary, i, range)
1729
+ if i == j
1730
+ i = downheap_maxside(pd, ary, i, range)
1731
+ if !range.include?(child1_maxside(i))
1732
+ if range.include?(j=child1_minside(i)) && pcmp(pd, ary, j, i) > 0
1733
+ swap(ary, i, j)
1734
+ i = j
1735
+ elsif range.include?(j=minside(i)) && pcmp(pd, ary, j, i) > 0
1736
+ swap(ary, i, j)
1737
+ i = j
1738
+ end
1739
+ end
1740
+ if minside?(i)
1741
+ i = upheap_minside(pd, ary, i, range)
1742
+ end
1743
+ end
1744
+ end
1745
+ i = upheap_sub(pd, ary, i, range)
1746
+ downheap_sub(pd, ary, i, range)
1747
+ end
1748
+
1749
+ def update_priority(pd, ary, loc, prio, subprio)
1750
+ i = loc.send(:index)
1751
+ ei, pi, si = get_entry(ary, i)
1752
+ subpriority ||= si
1753
+ set_entry(ary, i, ei, prio, subprio)
1754
+ range = 0...size(ary)
1755
+ adjust(pd, ary, i, range)
1756
+ end
1757
+
1758
+ def insert_internal(pd, ary, loc, prio, subprio)
1759
+ i = size(ary)
1760
+ set_entry(ary, i, loc, prio, subprio)
1761
+ range = 0...size(ary)
1762
+ adjust(pd, ary, i, range)
1763
+ end
1764
+
1765
+ def heapify(pd, ary, heapsize=0)
1766
+ currentsize = size(ary)
1767
+ h = Math.log(currentsize+1)/Math.log(2)
1768
+ if currentsize - 1 < (h - 1) * (currentsize - heapsize + 1)
1769
+ (currentsize-1).downto(0) {|i|
1770
+ adjust(pd, ary, i, i...currentsize)
1771
+ }
1772
+ else
1773
+ heapsize.upto(currentsize-1) {|i|
1774
+ adjust(pd, ary, i, 0...(i+1))
1775
+ }
1776
+ end
1777
+ currentsize
1778
+ end
1779
+
1780
+ def find_minmax_locator(pd, ary)
1781
+ case size(ary)
1782
+ when 0
1783
+ [nil, nil]
1784
+ when 1
1785
+ e0, p0, s0 = get_entry(ary, 0)
1786
+ [e0, e0]
1787
+ else
1788
+ if pcmp(pd, ary, 0, 1) == 0
1789
+ e0, p0, s0 = get_entry(ary, 0)
1790
+ [e0, e0]
1791
+ else
1792
+ e0, p0, s0 = get_entry(ary, 0)
1793
+ e1, p1, s1 = get_entry(ary, 1)
1794
+ [e0, e1]
1795
+ end
1796
+ end
1797
+ end
1798
+
1799
+ def find_min_locator(pd, ary)
1800
+ find_minmax_locator(pd, ary).first
1801
+ end
1802
+
1803
+ def find_max_locator(pd, ary)
1804
+ find_minmax_locator(pd, ary).last
1805
+ end
1806
+
1807
+ def delete_locator(pd, ary, loc)
1808
+ i = loc.send(:index)
1809
+ _, priority, subpriority = get_entry(ary, i)
1810
+ last = size(ary) - 1
1811
+ loc.send(:internal_deleted, priority, subpriority)
1812
+ el, pl, sl = delete_entry(ary, last)
1813
+ if i != last
1814
+ set_entry(ary, i, el, pl, sl)
1815
+ el.send(:index=, i)
1816
+ adjust(pd, ary, i, 0...last)
1817
+ end
1818
+ size(ary)
1819
+ end
1820
+ end
1821
+
1519
1822
  # :startdoc:
1520
1823
  end
data/test-depq.rb CHANGED
@@ -46,6 +46,30 @@ class Depq
46
46
  end
47
47
  end
48
48
 
49
+ def IntervalHeap.validation(pd, ary)
50
+ range=0...size(ary)
51
+ range.each {|j|
52
+ imin = parent_minside(j)
53
+ imax = parent_maxside(j)
54
+ jmin = minside(j)
55
+ if minside?(j) && range.include?(imin) && pcmp(pd, ary, imin, j) > 0
56
+ raise "ary[#{imin}].priority > ary[#{j}].priority "
57
+ end
58
+ if maxside?(j) && range.include?(imax) && pcmp(pd, ary, imax, j) < 0
59
+ raise "ary[#{imax}].priority < ary[#{j}].priority "
60
+ end
61
+ if range.include?(imin) && pcmp(pd, ary, imin, j) == 0 && scmp(pd, ary, imin, j) > 0
62
+ raise "ary[#{imin}].subpriority < ary[#{j}].subpriority "
63
+ end
64
+ if range.include?(imax) && pcmp(pd, ary, imax, j) == 0 && scmp(pd, ary, imax, j) > 0
65
+ raise "ary[#{imax}].subpriority < ary[#{j}].subpriority "
66
+ end
67
+ if maxside?(j) && range.include?(jmin) && pcmp(pd, ary, jmin, j) == 0 && scmp(pd, ary, jmin, j) > 0
68
+ raise "ary[#{jmin}].subpriority < ary[#{j}].subpriority "
69
+ end
70
+ }
71
+ end
72
+
49
73
  def validation
50
74
  @mode.validation(self, @ary) if @mode
51
75
  if @ary.length % ARY_SLICE_SIZE != 0
@@ -580,6 +604,28 @@ class TestDepq < Test::Unit::TestCase
580
604
  assert_equal([1, 3], res)
581
605
  end
582
606
 
607
+ def test_find_minmax_after_min
608
+ pd = Depq.new
609
+ assert_equal([nil, nil], pd.find_minmax)
610
+ pd.insert 3
611
+ pd.insert 1
612
+ pd.insert 2
613
+ assert_equal(1, pd.min)
614
+ res = pd.find_minmax
615
+ assert_equal([1, 3], res)
616
+ end
617
+
618
+ def test_find_minmax_after_max
619
+ pd = Depq.new
620
+ assert_equal([nil, nil], pd.find_minmax)
621
+ pd.insert 3
622
+ pd.insert 1
623
+ pd.insert 2
624
+ assert_equal(3, pd.max)
625
+ res = pd.find_minmax
626
+ assert_equal([1, 3], res)
627
+ end
628
+
583
629
  def test_delete_locator
584
630
  pd = Depq.new
585
631
  loc = pd.insert 1
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.1"
4
+ version: "0.2"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanaka Akira
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-20 00:00:00 +09:00
12
+ date: 2009-09-23 00:00:00 +09:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,7 +30,7 @@ files:
30
30
  - README
31
31
  - depq.rb
32
32
  has_rdoc: true
33
- homepage: http://www.a-k-r.org/depq/
33
+ homepage: http://depq.rubyforge.org/
34
34
  licenses: []
35
35
 
36
36
  post_install_message:
@@ -52,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
52
  version:
53
53
  requirements: []
54
54
 
55
- rubyforge_project:
55
+ rubyforge_project: depq
56
56
  rubygems_version: 1.3.4
57
57
  signing_key:
58
58
  specification_version: 3