lib/d_heap/benchmarks/implementations.rb in d_heap-0.4.0 vs lib/d_heap/benchmarks/implementations.rb in d_heap-0.5.0
- old
+ new
@@ -1,15 +1,21 @@
# frozen_string_literal: true
+require "fc"
+
module DHeap::Benchmarks
# base class for example priority queues
class ExamplePriorityQueue
attr_reader :a
- def initialize
+ # quick initialization by simply sorting the array once.
+ def initialize(count = nil, &block)
@a = []
+ return unless count
+ count.times {|i| @a << block.call(i) }
+ @a.sort!
end
def clear
@a.clear
end
@@ -96,73 +102,121 @@
# a very simple pure ruby binary heap
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
class RbHeap < ExamplePriorityQueue
- def <<(score)
- raise ArgumentError unless score
- @a.push(score)
+ def <<(value)
+ raise ArgumentError unless value
+ @a.push(value)
# shift up
index = @a.size - 1
while 0 < index # rubocop:disable Style/NumericPredicate
parent_index = (index - 1) / 2
- break if @a[parent_index] <= @a[index]
- @a[index] = @a[parent_index]
+ parent_value = @a[parent_index]
+ break if parent_value <= value
+ @a[index] = parent_value
index = parent_index
- @a[index] = score
- # check_heap!(index)
end
- self
+ @a[index] = value
+ # dbg "__push__(%p)" % [value]
+ # check_heap!(index)
end
def pop
return if @a.empty?
popped = @a.first
- @a[0] = shifting = @a.last
- @a.pop
- # shift down
- index = 0
+ value = @a.pop
last_index = @a.size - 1
- while (child_index = index * 2 + 1) <= last_index
+ last_parent = (last_index - 1) / 2
+ return popped unless 0 <= last_index
+
+ # sift down from 0
+ index = 0
+ child_index = 1
+ while index <= last_parent
+ child_value = @a[child_index]
# select min child
- if child_index < last_index && @a[child_index + 1] < @a[child_index]
- child_index += 1
+ if child_index < last_index
+ other_child_index = child_index + 1
+ other_child_value = @a[other_child_index]
+ if other_child_value < child_value
+ child_value = other_child_value
+ child_index = other_child_index
+ end
end
- break if @a[index] <= @a[child_index]
- @a[index] = @a[child_index]
+ break if value <= child_value
+ @a[index] = child_value
index = child_index
- @a[index] = shifting
+ child_index = index * 2 + 1
end
+ @a[index] = value
+
popped
end
private
- def check_heap!(idx, last = @a.size - 1)
- pscore = @a[idx]
- child = idx * 2 + 1
- if child <= last
- cscore = check_heap!(child)
- raise "#{pscore} > #{cscore}" if pscore > cscore
+ def check_heap!(idx)
+ check_heap_up!(idx)
+ check_heap_dn!(idx)
+ end
+
+ # compares index to its parent
+ def check_heap_at!(idx)
+ value = @a[idx]
+ unless idx <= 0
+ pidx = (idx - 1) / 2
+ pval = @a[pidx]
+ raise "@a[#{idx}] == #{value}, #{pval} > #{value}" if pval > value
end
- child += 1
- if child <= last
- check_heap!(child)
- cscore = check_heap!(child)
- raise "#{pscore} > #{cscore}" if pscore > cscore
- end
- pscore
+ value
end
+ def check_heap_up!(idx)
+ return if idx <= 0
+ pidx = (idx - 1) / 2
+ check_heap_at!(pidx)
+ check_heap_up!(pidx)
+ end
+
+ def check_heap_dn!(idx)
+ return unless @a.size <= idx
+ check_heap_at!(idx)
+ check_heap_down!(idx * 2 + 1)
+ check_heap_down!(idx * 2 + 2)
+ end
+
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
+ # minor adjustments to the "priority_queue_cxx" gem, to match the API
+ class CppSTL
+
+ def initialize
+ clear
+ end
+
+ def <<(value); @q.push(value, value) end
+
+ def clear
+ @q = FastContainers::PriorityQueue.new(:min)
+ end
+
+ def pop
+ @q.pop
+ rescue RuntimeError
+ nil
+ end
+
+ end
+
# Different duck-typed priority queue implemenations
IMPLEMENTATIONS = [
OpenStruct.new(name: " push and resort", klass: Sorting).freeze,
OpenStruct.new(name: " find min + del", klass: FindMin).freeze,
OpenStruct.new(name: "bsearch + insert", klass: BSearch).freeze,
OpenStruct.new(name: "ruby binary heap", klass: RbHeap).freeze,
+ OpenStruct.new(name: "C++STL PriorityQ", klass: CppSTL).freeze,
OpenStruct.new(name: "quaternary DHeap", klass: DHeap).freeze,
].freeze
end