stakach-algorithms 1.0.6 → 1.0.7
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.markdown +99 -97
- data/Rakefile +15 -27
- data/ext/algorithms/string/extconf.rb +4 -4
- data/ext/algorithms/string/string.c +68 -70
- data/ext/containers/bst/bst.c +249 -249
- data/ext/containers/bst/extconf.rb +4 -4
- data/ext/containers/deque/deque.c +248 -248
- data/ext/containers/deque/extconf.rb +4 -4
- data/ext/containers/rbtree_map/extconf.rb +4 -4
- data/ext/containers/rbtree_map/rbtree.c +500 -500
- data/ext/containers/splaytree_map/extconf.rb +4 -4
- data/ext/containers/splaytree_map/splaytree.c +421 -421
- data/lib/algorithms.rb +69 -69
- data/lib/algorithms/search.rb +85 -85
- data/lib/algorithms/sort.rb +242 -242
- data/lib/algorithms/string.rb +10 -10
- data/lib/algorithms/version.rb +3 -3
- data/lib/containers/deque.rb +176 -176
- data/lib/containers/heap.rb +501 -506
- data/lib/containers/kd_tree.rb +112 -112
- data/lib/containers/priority_queue.rb +116 -116
- data/lib/containers/queue.rb +71 -71
- data/lib/containers/rb_tree_map.rb +402 -402
- data/lib/containers/splay_tree_map.rb +273 -273
- data/lib/containers/stack.rb +70 -70
- data/lib/containers/suffix_array.rb +71 -71
- data/lib/containers/trie.rb +187 -187
- data/spec/bst_gc_mark_spec.rb +25 -0
- data/spec/bst_spec.rb +25 -0
- data/spec/deque_gc_mark_spec.rb +17 -0
- data/spec/deque_spec.rb +107 -0
- data/spec/heap_spec.rb +130 -0
- data/spec/helper.rb +4 -0
- data/spec/kd_expected_out.txt +10000 -0
- data/spec/kd_test_in.txt +10000 -0
- data/spec/kd_tree_spec.rb +33 -0
- data/spec/map_gc_mark_spec.rb +28 -0
- data/spec/priority_queue_spec.rb +74 -0
- data/spec/queue_spec.rb +60 -0
- data/spec/rb_tree_map_spec.rb +122 -0
- data/spec/search_spec.rb +27 -0
- data/spec/sort_spec.rb +26 -0
- data/spec/splay_tree_map_spec.rb +105 -0
- data/spec/stack_spec.rb +59 -0
- data/spec/string_spec.rb +12 -0
- data/spec/suffix_array_spec.rb +39 -0
- data/spec/trie_spec.rb +58 -0
- metadata +60 -4
data/lib/containers/kd_tree.rb
CHANGED
@@ -1,113 +1,113 @@
|
|
1
|
-
=begin rdoc
|
2
|
-
|
3
|
-
A kd-tree is a binary tree that allows one to store points (of any space dimension: 2D, 3D, etc).
|
4
|
-
The structure of the resulting tree makes it so that large portions of the tree are pruned
|
5
|
-
during queries.
|
6
|
-
|
7
|
-
One very good use of the tree is to allow nearest neighbor searching. Let's say you have a number
|
8
|
-
of points in 2D space, and you want to find the nearest 2 points from a specific point:
|
9
|
-
|
10
|
-
First, put the points into the tree:
|
11
|
-
|
12
|
-
kdtree = Algorithms::Containers::KDTree.new( {0 => [4, 3], 1 => [3, 4], 2 => [-1, 2], 3 => [6, 4],
|
13
|
-
4 => [3, -5], 5 => [-2, -5] })
|
14
|
-
|
15
|
-
Then, query on the tree:
|
16
|
-
|
17
|
-
puts kd.find_nearest([0, 0], 2) => [[5, 2], [9, 1]]
|
18
|
-
|
19
|
-
The result is an array of [distance, id] pairs. There seems to be a bug in this version.
|
20
|
-
|
21
|
-
Note that the point queried on does not have to exist in the tree. However, if it does exist,
|
22
|
-
it will be returned.
|
23
|
-
|
24
|
-
=end
|
25
|
-
module Algorithms
|
26
|
-
module Containers
|
27
|
-
class KDTree
|
28
|
-
Node = Struct.new(:id, :coords, :left, :right)
|
29
|
-
|
30
|
-
# Points is a hash of id => [coord, coord] pairs.
|
31
|
-
def initialize(points)
|
32
|
-
raise "must pass in a hash" unless points.kind_of?(Hash)
|
33
|
-
@dimensions = points[ points.keys.first ].size
|
34
|
-
@root = build_tree(points.to_a)
|
35
|
-
@nearest = []
|
36
|
-
end
|
37
|
-
|
38
|
-
# Find k closest points to given coordinates
|
39
|
-
def find_nearest(target, k_nearest)
|
40
|
-
@nearest = []
|
41
|
-
nearest(@root, target, k_nearest, 0)
|
42
|
-
end
|
43
|
-
|
44
|
-
# points is an array
|
45
|
-
def build_tree(points, depth=0)
|
46
|
-
return if points.empty?
|
47
|
-
|
48
|
-
axis = depth % @dimensions
|
49
|
-
|
50
|
-
points.sort! { |a, b| a.last[axis] <=> b.last[axis] }
|
51
|
-
median = points.size / 2
|
52
|
-
|
53
|
-
node = Node.new(points[median].first, points[median].last, nil, nil)
|
54
|
-
node.left = build_tree(points[0...median], depth+1)
|
55
|
-
node.right = build_tree(points[median+1..-1], depth+1)
|
56
|
-
node
|
57
|
-
end
|
58
|
-
private :build_tree
|
59
|
-
|
60
|
-
# Euclidian distanced, squared, between a node and target coords
|
61
|
-
def distance2(node, target)
|
62
|
-
return nil if node.nil? or target.nil?
|
63
|
-
c = (node.coords[0] - target[0])
|
64
|
-
d = (node.coords[1] - target[1])
|
65
|
-
c * c + d * d
|
66
|
-
end
|
67
|
-
private :distance2
|
68
|
-
|
69
|
-
# Update array of nearest elements if necessary
|
70
|
-
def check_nearest(nearest, node, target, k_nearest)
|
71
|
-
d = distance2(node, target)
|
72
|
-
if nearest.size < k_nearest || d < nearest.last[0]
|
73
|
-
nearest.pop if nearest.size >= k_nearest
|
74
|
-
nearest << [d, node.id]
|
75
|
-
nearest.sort! { |a, b| a[0] <=> b[0] }
|
76
|
-
end
|
77
|
-
nearest
|
78
|
-
end
|
79
|
-
private :check_nearest
|
80
|
-
|
81
|
-
# Recursively find nearest coordinates, going down the appropriate branch as needed
|
82
|
-
def nearest(node, target, k_nearest, depth)
|
83
|
-
axis = depth % @dimensions
|
84
|
-
|
85
|
-
if node.left.nil? && node.right.nil? # Leaf node
|
86
|
-
@nearest = check_nearest(@nearest, node, target, k_nearest)
|
87
|
-
return
|
88
|
-
end
|
89
|
-
|
90
|
-
# Go down the nearest split
|
91
|
-
if node.right.nil? || (node.left && target[axis] <= node.coords[axis])
|
92
|
-
nearer = node.left
|
93
|
-
further = node.right
|
94
|
-
else
|
95
|
-
nearer = node.right
|
96
|
-
further = node.left
|
97
|
-
end
|
98
|
-
nearest(nearer, target, k_nearest, depth+1)
|
99
|
-
|
100
|
-
# See if we have to check other side
|
101
|
-
if further
|
102
|
-
if @nearest.size < k_nearest || (target[axis] - node.coords[axis])**2 < @nearest.last[0]
|
103
|
-
nearest(further, target, k_nearest, depth+1)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
@nearest = check_nearest(@nearest, node, target, k_nearest)
|
108
|
-
end
|
109
|
-
private :nearest
|
110
|
-
|
111
|
-
end
|
112
|
-
end
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
A kd-tree is a binary tree that allows one to store points (of any space dimension: 2D, 3D, etc).
|
4
|
+
The structure of the resulting tree makes it so that large portions of the tree are pruned
|
5
|
+
during queries.
|
6
|
+
|
7
|
+
One very good use of the tree is to allow nearest neighbor searching. Let's say you have a number
|
8
|
+
of points in 2D space, and you want to find the nearest 2 points from a specific point:
|
9
|
+
|
10
|
+
First, put the points into the tree:
|
11
|
+
|
12
|
+
kdtree = Algorithms::Containers::KDTree.new( {0 => [4, 3], 1 => [3, 4], 2 => [-1, 2], 3 => [6, 4],
|
13
|
+
4 => [3, -5], 5 => [-2, -5] })
|
14
|
+
|
15
|
+
Then, query on the tree:
|
16
|
+
|
17
|
+
puts kd.find_nearest([0, 0], 2) => [[5, 2], [9, 1]]
|
18
|
+
|
19
|
+
The result is an array of [distance, id] pairs. There seems to be a bug in this version.
|
20
|
+
|
21
|
+
Note that the point queried on does not have to exist in the tree. However, if it does exist,
|
22
|
+
it will be returned.
|
23
|
+
|
24
|
+
=end
|
25
|
+
module Algorithms
|
26
|
+
module Containers
|
27
|
+
class KDTree
|
28
|
+
Node = Struct.new(:id, :coords, :left, :right)
|
29
|
+
|
30
|
+
# Points is a hash of id => [coord, coord] pairs.
|
31
|
+
def initialize(points)
|
32
|
+
raise "must pass in a hash" unless points.kind_of?(Hash)
|
33
|
+
@dimensions = points[ points.keys.first ].size
|
34
|
+
@root = build_tree(points.to_a)
|
35
|
+
@nearest = []
|
36
|
+
end
|
37
|
+
|
38
|
+
# Find k closest points to given coordinates
|
39
|
+
def find_nearest(target, k_nearest)
|
40
|
+
@nearest = []
|
41
|
+
nearest(@root, target, k_nearest, 0)
|
42
|
+
end
|
43
|
+
|
44
|
+
# points is an array
|
45
|
+
def build_tree(points, depth=0)
|
46
|
+
return if points.empty?
|
47
|
+
|
48
|
+
axis = depth % @dimensions
|
49
|
+
|
50
|
+
points.sort! { |a, b| a.last[axis] <=> b.last[axis] }
|
51
|
+
median = points.size / 2
|
52
|
+
|
53
|
+
node = Node.new(points[median].first, points[median].last, nil, nil)
|
54
|
+
node.left = build_tree(points[0...median], depth+1)
|
55
|
+
node.right = build_tree(points[median+1..-1], depth+1)
|
56
|
+
node
|
57
|
+
end
|
58
|
+
private :build_tree
|
59
|
+
|
60
|
+
# Euclidian distanced, squared, between a node and target coords
|
61
|
+
def distance2(node, target)
|
62
|
+
return nil if node.nil? or target.nil?
|
63
|
+
c = (node.coords[0] - target[0])
|
64
|
+
d = (node.coords[1] - target[1])
|
65
|
+
c * c + d * d
|
66
|
+
end
|
67
|
+
private :distance2
|
68
|
+
|
69
|
+
# Update array of nearest elements if necessary
|
70
|
+
def check_nearest(nearest, node, target, k_nearest)
|
71
|
+
d = distance2(node, target)
|
72
|
+
if nearest.size < k_nearest || d < nearest.last[0]
|
73
|
+
nearest.pop if nearest.size >= k_nearest
|
74
|
+
nearest << [d, node.id]
|
75
|
+
nearest.sort! { |a, b| a[0] <=> b[0] }
|
76
|
+
end
|
77
|
+
nearest
|
78
|
+
end
|
79
|
+
private :check_nearest
|
80
|
+
|
81
|
+
# Recursively find nearest coordinates, going down the appropriate branch as needed
|
82
|
+
def nearest(node, target, k_nearest, depth)
|
83
|
+
axis = depth % @dimensions
|
84
|
+
|
85
|
+
if node.left.nil? && node.right.nil? # Leaf node
|
86
|
+
@nearest = check_nearest(@nearest, node, target, k_nearest)
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
# Go down the nearest split
|
91
|
+
if node.right.nil? || (node.left && target[axis] <= node.coords[axis])
|
92
|
+
nearer = node.left
|
93
|
+
further = node.right
|
94
|
+
else
|
95
|
+
nearer = node.right
|
96
|
+
further = node.left
|
97
|
+
end
|
98
|
+
nearest(nearer, target, k_nearest, depth+1)
|
99
|
+
|
100
|
+
# See if we have to check other side
|
101
|
+
if further
|
102
|
+
if @nearest.size < k_nearest || (target[axis] - node.coords[axis])**2 < @nearest.last[0]
|
103
|
+
nearest(further, target, k_nearest, depth+1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@nearest = check_nearest(@nearest, node, target, k_nearest)
|
108
|
+
end
|
109
|
+
private :nearest
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
113
|
end
|
@@ -1,117 +1,117 @@
|
|
1
|
-
require 'containers/heap'
|
2
|
-
|
3
|
-
=begin rdoc
|
4
|
-
A Priority Queue is a data structure that behaves like a queue except that elements have an
|
5
|
-
associated priority. The #next and #pop methods return the item with the next highest priority.
|
6
|
-
|
7
|
-
Priority Queues are often used in graph problems, such as Dijkstra's Algorithm for shortest
|
8
|
-
path, and the A* search algorithm for shortest path.
|
9
|
-
|
10
|
-
This container is implemented using the Fibonacci heap included in the Collections library.
|
11
|
-
=end
|
12
|
-
module Algorithms
|
13
|
-
module Containers
|
14
|
-
class PriorityQueue
|
15
|
-
include Enumerable
|
16
|
-
|
17
|
-
# Create a new, empty PriorityQueue
|
18
|
-
def initialize(&block)
|
19
|
-
# We default to a priority queue that returns the largest value
|
20
|
-
block ||= lambda { |x, y| (x <=> y) == 1 }
|
21
|
-
@heap = Heap.new(&block)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Returns the number of elements in the queue.
|
25
|
-
#
|
26
|
-
# q = Algorithms::Containers::PriorityQueue.new
|
27
|
-
# q.size #=> 0
|
28
|
-
# q.push("Alaska", 1)
|
29
|
-
# q.size #=> 1
|
30
|
-
def size
|
31
|
-
@heap.size
|
32
|
-
end
|
33
|
-
alias_method :length, :size
|
34
|
-
|
35
|
-
# Add an object to the queue with associated priority.
|
36
|
-
#
|
37
|
-
# q = Algorithms::Containers::PriorityQueue.new
|
38
|
-
# q.push("Alaska", 1)
|
39
|
-
# q.pop #=> "Alaska"
|
40
|
-
def push(object, priority)
|
41
|
-
@heap.push(priority, object)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Clears all the items in the queue.
|
45
|
-
def clear
|
46
|
-
@heap.clear
|
47
|
-
end
|
48
|
-
|
49
|
-
# Returns true if the queue is empty, false otherwise.
|
50
|
-
def empty?
|
51
|
-
@heap.empty?
|
52
|
-
end
|
53
|
-
|
54
|
-
# call-seq:
|
55
|
-
# has_priority? priority -> boolean
|
56
|
-
#
|
57
|
-
# Return true if the priority is in the queue, false otherwise.
|
58
|
-
#
|
59
|
-
# q = PriorityQueue.new
|
60
|
-
# q.push("Alaska", 1)
|
61
|
-
#
|
62
|
-
# q.has_priority?(1) #=> true
|
63
|
-
# q.has_priority?(2) #=> false
|
64
|
-
def has_priority?(priority)
|
65
|
-
@heap.has_key?(priority)
|
66
|
-
end
|
67
|
-
|
68
|
-
# call-seq:
|
69
|
-
# next -> object
|
70
|
-
#
|
71
|
-
# Return the object with the next highest priority, but does not remove it
|
72
|
-
#
|
73
|
-
# q = Algorithms::Containers::PriorityQueue.new
|
74
|
-
# q.push("Alaska", 50)
|
75
|
-
# q.push("Delaware", 30)
|
76
|
-
# q.push("Georgia", 35)
|
77
|
-
# q.next #=> "Alaska"
|
78
|
-
def next
|
79
|
-
@heap.next
|
80
|
-
end
|
81
|
-
|
82
|
-
# call-seq:
|
83
|
-
# pop -> object
|
84
|
-
#
|
85
|
-
# Return the object with the next highest priority and removes it from the queue
|
86
|
-
#
|
87
|
-
# q = Algorithms::Containers::PriorityQueue.new
|
88
|
-
# q.push("Alaska", 50)
|
89
|
-
# q.push("Delaware", 30)
|
90
|
-
# q.push("Georgia", 35)
|
91
|
-
# q.pop #=> "Alaska"
|
92
|
-
# q.size #=> 2
|
93
|
-
def pop
|
94
|
-
@heap.pop
|
95
|
-
end
|
96
|
-
alias_method :next!, :pop
|
97
|
-
|
98
|
-
# call-seq:
|
99
|
-
# delete(priority) -> object
|
100
|
-
# delete(priority) -> nil
|
101
|
-
#
|
102
|
-
# Delete an object with specified priority from the queue. If there are duplicates, an
|
103
|
-
# arbitrary object with that priority is deleted and returned. Returns nil if there are
|
104
|
-
# no objects with the priority.
|
105
|
-
#
|
106
|
-
# q = PriorityQueue.new
|
107
|
-
# q.push("Alaska", 50)
|
108
|
-
# q.push("Delaware", 30)
|
109
|
-
# q.delete(50) #=> "Alaska"
|
110
|
-
# q.delete(10) #=> nil
|
111
|
-
def delete(priority)
|
112
|
-
@heap.delete(priority)
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
1
|
+
require 'containers/heap'
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
A Priority Queue is a data structure that behaves like a queue except that elements have an
|
5
|
+
associated priority. The #next and #pop methods return the item with the next highest priority.
|
6
|
+
|
7
|
+
Priority Queues are often used in graph problems, such as Dijkstra's Algorithm for shortest
|
8
|
+
path, and the A* search algorithm for shortest path.
|
9
|
+
|
10
|
+
This container is implemented using the Fibonacci heap included in the Collections library.
|
11
|
+
=end
|
12
|
+
module Algorithms
|
13
|
+
module Containers
|
14
|
+
class PriorityQueue
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
# Create a new, empty PriorityQueue
|
18
|
+
def initialize(&block)
|
19
|
+
# We default to a priority queue that returns the largest value
|
20
|
+
block ||= lambda { |x, y| (x <=> y) == 1 }
|
21
|
+
@heap = Heap.new(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the number of elements in the queue.
|
25
|
+
#
|
26
|
+
# q = Algorithms::Containers::PriorityQueue.new
|
27
|
+
# q.size #=> 0
|
28
|
+
# q.push("Alaska", 1)
|
29
|
+
# q.size #=> 1
|
30
|
+
def size
|
31
|
+
@heap.size
|
32
|
+
end
|
33
|
+
alias_method :length, :size
|
34
|
+
|
35
|
+
# Add an object to the queue with associated priority.
|
36
|
+
#
|
37
|
+
# q = Algorithms::Containers::PriorityQueue.new
|
38
|
+
# q.push("Alaska", 1)
|
39
|
+
# q.pop #=> "Alaska"
|
40
|
+
def push(object, priority)
|
41
|
+
@heap.push(priority, object)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Clears all the items in the queue.
|
45
|
+
def clear
|
46
|
+
@heap.clear
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns true if the queue is empty, false otherwise.
|
50
|
+
def empty?
|
51
|
+
@heap.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
# call-seq:
|
55
|
+
# has_priority? priority -> boolean
|
56
|
+
#
|
57
|
+
# Return true if the priority is in the queue, false otherwise.
|
58
|
+
#
|
59
|
+
# q = PriorityQueue.new
|
60
|
+
# q.push("Alaska", 1)
|
61
|
+
#
|
62
|
+
# q.has_priority?(1) #=> true
|
63
|
+
# q.has_priority?(2) #=> false
|
64
|
+
def has_priority?(priority)
|
65
|
+
@heap.has_key?(priority)
|
66
|
+
end
|
67
|
+
|
68
|
+
# call-seq:
|
69
|
+
# next -> object
|
70
|
+
#
|
71
|
+
# Return the object with the next highest priority, but does not remove it
|
72
|
+
#
|
73
|
+
# q = Algorithms::Containers::PriorityQueue.new
|
74
|
+
# q.push("Alaska", 50)
|
75
|
+
# q.push("Delaware", 30)
|
76
|
+
# q.push("Georgia", 35)
|
77
|
+
# q.next #=> "Alaska"
|
78
|
+
def next
|
79
|
+
@heap.next
|
80
|
+
end
|
81
|
+
|
82
|
+
# call-seq:
|
83
|
+
# pop -> object
|
84
|
+
#
|
85
|
+
# Return the object with the next highest priority and removes it from the queue
|
86
|
+
#
|
87
|
+
# q = Algorithms::Containers::PriorityQueue.new
|
88
|
+
# q.push("Alaska", 50)
|
89
|
+
# q.push("Delaware", 30)
|
90
|
+
# q.push("Georgia", 35)
|
91
|
+
# q.pop #=> "Alaska"
|
92
|
+
# q.size #=> 2
|
93
|
+
def pop
|
94
|
+
@heap.pop
|
95
|
+
end
|
96
|
+
alias_method :next!, :pop
|
97
|
+
|
98
|
+
# call-seq:
|
99
|
+
# delete(priority) -> object
|
100
|
+
# delete(priority) -> nil
|
101
|
+
#
|
102
|
+
# Delete an object with specified priority from the queue. If there are duplicates, an
|
103
|
+
# arbitrary object with that priority is deleted and returned. Returns nil if there are
|
104
|
+
# no objects with the priority.
|
105
|
+
#
|
106
|
+
# q = PriorityQueue.new
|
107
|
+
# q.push("Alaska", 50)
|
108
|
+
# q.push("Delaware", 30)
|
109
|
+
# q.delete(50) #=> "Alaska"
|
110
|
+
# q.delete(10) #=> nil
|
111
|
+
def delete(priority)
|
112
|
+
@heap.delete(priority)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
117
|
end
|