stakach-algorithms 1.0.6 → 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|