From 086ec74767a44df5beed64ccc35e67d0534a0552 Mon Sep 17 00:00:00 2001 From: pjs <pjs@alum.mit.edu> Date: Fri, 19 Aug 2016 15:02:00 -0700 Subject: [PATCH] Array-based priority queue --- lib/priority_queue.rb | 89 +++++++++++++++++++++++++++++++++++++++++++ lib/simplekit.rb | 13 +++---- 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 lib/priority_queue.rb diff --git a/lib/priority_queue.rb b/lib/priority_queue.rb new file mode 100644 index 0000000..d638406 --- /dev/null +++ b/lib/priority_queue.rb @@ -0,0 +1,89 @@ +# dis is a PriorityQueue +class PriorityQueue + def initialize + clear + end + + def <<(element) + @elements << element + # bubble up the element that we just added + bubble_up(@elements.size - 1) + end + + alias push << + + def peek + # the first element will always be the min, because of the heap constraint + @elements[1] + end + + def pop + # remove the last element of the list + min = @elements[1] + + # and make sure the tree is ordered again + bubble_down(1) + min + end + + def clear + @elements = [nil] + end + + def empty? + @elements.length < 2 + end + + private + + def bubble_up(index) + target = @elements[index] + loop do + parent_index = (index / 2) + + # return if we reach the root or the parent is less than the child + if parent_index < 1 || @elements[parent_index] <= target + @elements[index] = target + return + end + + # otherwise we exchange the child with the parent + @elements[index] = @elements[parent_index] + + # and keep bubbling up + index = parent_index + end + end + + def bubble_down(index) + target = @elements.pop + loop do + child_index = (index * 2) + + # stop if we reach the bottom of the tree + if child_index > @elements.size - 1 + @elements[index] = target + return + end + + # make sure we get the smallest child + not_the_last_element = child_index < @elements.size - 1 + left_element = @elements[child_index] + right_element = @elements[child_index + 1] + child_index += 1 if not_the_last_element && right_element < left_element + + # there is no need to continue if the parent element is already smaller + # then its children + if target <= @elements[child_index] + @elements[index] = target + return + end + + @elements[index] = @elements[child_index] + + # repeat the process until we reach a point where the parent + # is larger than its children + index = child_index + end + end +end diff --git a/lib/simplekit.rb b/lib/simplekit.rb index 07cdc7f..cf428b4 100644 --- a/lib/simplekit.rb +++ b/lib/simplekit.rb @@ -1,5 +1,4 @@ -require 'rubygems' if RUBY_VERSION =~ /^1\.8/ -require 'skewheap' +require_relative 'priority_queue' # The +SimpleKit+ module provides basic event scheduling capabilities. # @@ -10,7 +9,7 @@ require 'skewheap' module SimpleKit # The set of module methods to be passed to the EventScheduler # if not found in the model class. - DELEGATED_METHODS = [:model_time, :schedule, :halt] + DELEGATED_METHODS = [:model_time, :schedule, :halt].freeze # Run your model by creating a new +EventScheduler+ and invoking its # +run+ method. @@ -29,8 +28,8 @@ module SimpleKit end end - # Class +EventScheduler+ provides the computation engine for a discrete - # event simulation model. It uses the +SkewHeap+ RubyGem as a priority + # Class +EventScheduler+ provides the computation engine for a + # discrete event simulation model. It uses an array-based priority # queue implementation for the pending events list. # # Users must create a model class which: @@ -43,7 +42,7 @@ module SimpleKit # and setting up an empty event list. def initialize(the_model) @user_model = the_model - @event_list = SkewHeap.new + @event_list = PriorityQueue.new end # Add an event to the pending events list. @@ -80,8 +79,6 @@ module SimpleKit end end - private - # This is a private helper Struct for the EventScheduler class. # Users should never try to access this directly. EventNotice = Struct.new(:event, :time, *:args) do -- GitLab