mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
367 lines
7.3 KiB
Ruby
367 lines
7.3 KiB
Ruby
|
# This benchmark is imported from https://github.com/jruby/rubybench/blob/master/time/bench_red_black.rb
|
||
|
# License is License is Apache-2
|
||
|
|
||
|
require 'benchmark'
|
||
|
|
||
|
# Algorithm based on "Introduction to Algorithms" by Cormen and others
|
||
|
class RedBlackTree
|
||
|
class Node
|
||
|
attr_accessor :color
|
||
|
attr_accessor :key
|
||
|
attr_accessor :left
|
||
|
attr_accessor :right
|
||
|
attr_accessor :parent
|
||
|
|
||
|
RED = :red
|
||
|
BLACK = :black
|
||
|
COLORS = [RED, BLACK].freeze
|
||
|
|
||
|
def initialize(key, color = RED)
|
||
|
raise ArgumentError, "Bad value for color parameter" unless COLORS.include?(color)
|
||
|
@color = color
|
||
|
@key = key
|
||
|
@left = @right = @parent = NilNode.instance
|
||
|
end
|
||
|
|
||
|
def black?
|
||
|
return color == BLACK
|
||
|
end
|
||
|
|
||
|
def red?
|
||
|
return color == RED
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class NilNode < Node
|
||
|
class << self
|
||
|
private :new
|
||
|
|
||
|
# it's not thread safe
|
||
|
def instance
|
||
|
@instance ||= begin
|
||
|
def instance
|
||
|
return @instance
|
||
|
end
|
||
|
|
||
|
new
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def initialize
|
||
|
self.color = BLACK
|
||
|
self.key = 0
|
||
|
self.left = nil
|
||
|
self.right = nil
|
||
|
self.parent = nil
|
||
|
end
|
||
|
|
||
|
def nil?
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
include Enumerable
|
||
|
|
||
|
attr_accessor :root
|
||
|
attr_accessor :size
|
||
|
|
||
|
def initialize
|
||
|
self.root = NilNode.instance
|
||
|
self.size = 0
|
||
|
end
|
||
|
|
||
|
def add(key)
|
||
|
insert(Node.new(key))
|
||
|
end
|
||
|
|
||
|
def insert(x)
|
||
|
insert_helper(x)
|
||
|
|
||
|
x.color = Node::RED
|
||
|
while x != root && x.parent.color == Node::RED
|
||
|
if x.parent == x.parent.parent.left
|
||
|
y = x.parent.parent.right
|
||
|
if !y.nil? && y.color == Node::RED
|
||
|
x.parent.color = Node::BLACK
|
||
|
y.color = Node::BLACK
|
||
|
x.parent.parent.color = Node::RED
|
||
|
x = x.parent.parent
|
||
|
else
|
||
|
if x == x.parent.right
|
||
|
x = x.parent
|
||
|
left_rotate(x)
|
||
|
end
|
||
|
x.parent.color = Node::BLACK
|
||
|
x.parent.parent.color = Node::RED
|
||
|
right_rotate(x.parent.parent)
|
||
|
end
|
||
|
else
|
||
|
y = x.parent.parent.left
|
||
|
if !y.nil? && y.color == Node::RED
|
||
|
x.parent.color = Node::BLACK
|
||
|
y.color = Node::BLACK
|
||
|
x.parent.parent.color = Node::RED
|
||
|
x = x.parent.parent
|
||
|
else
|
||
|
if x == x.parent.left
|
||
|
x = x.parent
|
||
|
right_rotate(x)
|
||
|
end
|
||
|
x.parent.color = Node::BLACK
|
||
|
x.parent.parent.color = Node::RED
|
||
|
left_rotate(x.parent.parent)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
root.color = Node::BLACK
|
||
|
end
|
||
|
|
||
|
alias << insert
|
||
|
|
||
|
def delete(z)
|
||
|
y = (z.left.nil? || z.right.nil?) ? z : successor(z)
|
||
|
x = y.left.nil? ? y.right : y.left
|
||
|
x.parent = y.parent
|
||
|
|
||
|
if y.parent.nil?
|
||
|
self.root = x
|
||
|
else
|
||
|
if y == y.parent.left
|
||
|
y.parent.left = x
|
||
|
else
|
||
|
y.parent.right = x
|
||
|
end
|
||
|
end
|
||
|
|
||
|
z.key = y.key if y != z
|
||
|
|
||
|
if y.color == Node::BLACK
|
||
|
delete_fixup(x)
|
||
|
end
|
||
|
|
||
|
self.size -= 1
|
||
|
return y
|
||
|
end
|
||
|
|
||
|
def minimum(x = root)
|
||
|
while !x.left.nil?
|
||
|
x = x.left
|
||
|
end
|
||
|
return x
|
||
|
end
|
||
|
|
||
|
def maximum(x = root)
|
||
|
while !x.right.nil?
|
||
|
x = x.right
|
||
|
end
|
||
|
return x
|
||
|
end
|
||
|
|
||
|
def successor(x)
|
||
|
if !x.right.nil?
|
||
|
return minimum(x.right)
|
||
|
end
|
||
|
y = x.parent
|
||
|
while !y.nil? && x == y.right
|
||
|
x = y
|
||
|
y = y.parent
|
||
|
end
|
||
|
return y
|
||
|
end
|
||
|
|
||
|
def predecessor(x)
|
||
|
if !x.left.nil?
|
||
|
return maximum(x.left)
|
||
|
end
|
||
|
y = x.parent
|
||
|
while !y.nil? && x == y.left
|
||
|
x = y
|
||
|
y = y.parent
|
||
|
end
|
||
|
return y
|
||
|
end
|
||
|
|
||
|
def inorder_walk(x = root)
|
||
|
x = self.minimum
|
||
|
while !x.nil?
|
||
|
yield x.key
|
||
|
x = successor(x)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
alias each inorder_walk
|
||
|
|
||
|
def reverse_inorder_walk(x = root)
|
||
|
x = self.maximum
|
||
|
while !x.nil?
|
||
|
yield x.key
|
||
|
x = predecessor(x)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
alias reverse_each reverse_inorder_walk
|
||
|
|
||
|
def search(key, x = root)
|
||
|
while !x.nil? && x.key != key
|
||
|
key < x.key ? x = x.left : x = x.right
|
||
|
end
|
||
|
return x
|
||
|
end
|
||
|
|
||
|
def empty?
|
||
|
return self.root.nil?
|
||
|
end
|
||
|
|
||
|
def black_height(x = root)
|
||
|
height = 0
|
||
|
while !x.nil?
|
||
|
x = x.left
|
||
|
height +=1 if x.nil? || x.black?
|
||
|
end
|
||
|
return height
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def left_rotate(x)
|
||
|
raise "x.right is nil!" if x.right.nil?
|
||
|
y = x.right
|
||
|
x.right = y.left
|
||
|
y.left.parent = x if !y.left.nil?
|
||
|
y.parent = x.parent
|
||
|
if x.parent.nil?
|
||
|
self.root = y
|
||
|
else
|
||
|
if x == x.parent.left
|
||
|
x.parent.left = y
|
||
|
else
|
||
|
x.parent.right = y
|
||
|
end
|
||
|
end
|
||
|
y.left = x
|
||
|
x.parent = y
|
||
|
end
|
||
|
|
||
|
def right_rotate(x)
|
||
|
raise "x.left is nil!" if x.left.nil?
|
||
|
y = x.left
|
||
|
x.left = y.right
|
||
|
y.right.parent = x if !y.right.nil?
|
||
|
y.parent = x.parent
|
||
|
if x.parent.nil?
|
||
|
self.root = y
|
||
|
else
|
||
|
if x == x.parent.left
|
||
|
x.parent.left = y
|
||
|
else
|
||
|
x.parent.right = y
|
||
|
end
|
||
|
end
|
||
|
y.right = x
|
||
|
x.parent = y
|
||
|
end
|
||
|
|
||
|
def insert_helper(z)
|
||
|
y = NilNode.instance
|
||
|
x = root
|
||
|
while !x.nil?
|
||
|
y = x
|
||
|
z.key < x.key ? x = x.left : x = x.right
|
||
|
end
|
||
|
z.parent = y
|
||
|
if y.nil?
|
||
|
self.root = z
|
||
|
else
|
||
|
z.key < y.key ? y.left = z : y.right = z
|
||
|
end
|
||
|
self.size += 1
|
||
|
end
|
||
|
|
||
|
def delete_fixup(x)
|
||
|
while x != root && x.color == Node::BLACK
|
||
|
if x == x.parent.left
|
||
|
w = x.parent.right
|
||
|
if w.color == Node::RED
|
||
|
w.color = Node::BLACK
|
||
|
x.parent.color = Node::RED
|
||
|
left_rotate(x.parent)
|
||
|
w = x.parent.right
|
||
|
end
|
||
|
if w.left.color == Node::BLACK && w.right.color == Node::BLACK
|
||
|
w.color = Node::RED
|
||
|
x = x.parent
|
||
|
else
|
||
|
if w.right.color == Node::BLACK
|
||
|
w.left.color = Node::BLACK
|
||
|
w.color = Node::RED
|
||
|
right_rotate(w)
|
||
|
w = x.parent.right
|
||
|
end
|
||
|
w.color = x.parent.color
|
||
|
x.parent.color = Node::BLACK
|
||
|
w.right.color = Node::BLACK
|
||
|
left_rotate(x.parent)
|
||
|
x = root
|
||
|
end
|
||
|
else
|
||
|
w = x.parent.left
|
||
|
if w.color == Node::RED
|
||
|
w.color = Node::BLACK
|
||
|
x.parent.color = Node::RED
|
||
|
right_rotate(x.parent)
|
||
|
w = x.parent.left
|
||
|
end
|
||
|
if w.right.color == Node::BLACK && w.left.color == Node::BLACK
|
||
|
w.color = Node::RED
|
||
|
x = x.parent
|
||
|
else
|
||
|
if w.left.color == Node::BLACK
|
||
|
w.right.color = Node::BLACK
|
||
|
w.color = Node::RED
|
||
|
left_rotate(w)
|
||
|
w = x.parent.left
|
||
|
end
|
||
|
w.color = x.parent.color
|
||
|
x.parent.color = Node::BLACK
|
||
|
w.left.color = Node::BLACK
|
||
|
right_rotate(x.parent)
|
||
|
x = root
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
x.color = Node::BLACK
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def rbt_bm
|
||
|
n = 100_000
|
||
|
a1 = []; n.times { a1 << rand(999_999) }
|
||
|
a2 = []; n.times { a2 << rand(999_999) }
|
||
|
|
||
|
start = Time.now
|
||
|
|
||
|
tree = RedBlackTree.new
|
||
|
|
||
|
n.times {|i| tree.add(i) }
|
||
|
n.times { tree.delete(tree.root) }
|
||
|
|
||
|
tree = RedBlackTree.new
|
||
|
a1.each {|e| tree.add(e) }
|
||
|
a2.each {|e| tree.search(e) }
|
||
|
tree.inorder_walk {|key| key + 1 }
|
||
|
tree.reverse_inorder_walk {|key| key + 1 }
|
||
|
n.times { tree.minimum }
|
||
|
n.times { tree.maximum }
|
||
|
|
||
|
return Time.now - start
|
||
|
end
|
||
|
|
||
|
N = (ARGV[0] || 10).to_i
|
||
|
|
||
|
N.times do
|
||
|
# puts rbt_bm.to_f
|
||
|
rbt_bm.to_f
|
||
|
# puts "GC.count = #{GC.count}" if GC.respond_to?(:count)
|
||
|
end
|