stakach-algorithms 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,72 +1,72 @@
1
- require 'containers/deque'
2
-
3
- =begin rdoc
4
- A Queue is a container that keeps elements in a first-in first-out (FIFO) order. Because of its
5
- properties, it is often used as a buffer.
6
-
7
- This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
8
-
9
- =end
10
- module Algorithms
11
- module Containers
12
- class Queue
13
- include Enumerable
14
- # Create a new queue. Takes an optional array argument to initialize the queue.
15
- #
16
- # q = Algorithms::Containers::Queue.new([1, 2, 3])
17
- # q.pop #=> 1
18
- # q.pop #=> 2
19
- def initialize(ary=[])
20
- @container = Deque.new(ary)
21
- end
22
-
23
- # Returns the next item from the queue but does not remove it.
24
- #
25
- # q = Algorithms::Containers::Queue.new([1, 2, 3])
26
- # q.next #=> 1
27
- # q.size #=> 3
28
- def next
29
- @container.front
30
- end
31
-
32
- # Adds an item to the queue.
33
- #
34
- # q = Algorithms::Containers::Queue.new([1])
35
- # q.push(2)
36
- # q.pop #=> 1
37
- # q.pop #=> 2
38
- def push(obj)
39
- @container.push_back(obj)
40
- end
41
- alias_method :<<, :push
42
-
43
- # Removes the next item from the queue and returns it.
44
- #
45
- # q = Algorithms::Containers::Queue.new([1, 2, 3])
46
- # q.pop #=> 1
47
- # q.size #=> 2
48
- def pop
49
- @container.pop_front
50
- end
51
-
52
- # Return the number of items in the queue.
53
- #
54
- # q = Algorithms::Containers::Queue.new([1, 2, 3])
55
- # q.size #=> 3
56
- def size
57
- @container.size
58
- end
59
-
60
- # Returns true if the queue is empty, false otherwise.
61
- def empty?
62
- @container.empty?
63
- end
64
-
65
- # Iterate over the Queue in FIFO order.
66
- def each(&block)
67
- @container.each_forward(&block)
68
- end
69
-
70
- end
71
- end
1
+ require 'containers/deque'
2
+
3
+ =begin rdoc
4
+ A Queue is a container that keeps elements in a first-in first-out (FIFO) order. Because of its
5
+ properties, it is often used as a buffer.
6
+
7
+ This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
8
+
9
+ =end
10
+ module Algorithms
11
+ module Containers
12
+ class Queue
13
+ include Enumerable
14
+ # Create a new queue. Takes an optional array argument to initialize the queue.
15
+ #
16
+ # q = Algorithms::Containers::Queue.new([1, 2, 3])
17
+ # q.pop #=> 1
18
+ # q.pop #=> 2
19
+ def initialize(ary=[])
20
+ @container = Deque.new(ary)
21
+ end
22
+
23
+ # Returns the next item from the queue but does not remove it.
24
+ #
25
+ # q = Algorithms::Containers::Queue.new([1, 2, 3])
26
+ # q.next #=> 1
27
+ # q.size #=> 3
28
+ def next
29
+ @container.front
30
+ end
31
+
32
+ # Adds an item to the queue.
33
+ #
34
+ # q = Algorithms::Containers::Queue.new([1])
35
+ # q.push(2)
36
+ # q.pop #=> 1
37
+ # q.pop #=> 2
38
+ def push(obj)
39
+ @container.push_back(obj)
40
+ end
41
+ alias_method :<<, :push
42
+
43
+ # Removes the next item from the queue and returns it.
44
+ #
45
+ # q = Algorithms::Containers::Queue.new([1, 2, 3])
46
+ # q.pop #=> 1
47
+ # q.size #=> 2
48
+ def pop
49
+ @container.pop_front
50
+ end
51
+
52
+ # Return the number of items in the queue.
53
+ #
54
+ # q = Algorithms::Containers::Queue.new([1, 2, 3])
55
+ # q.size #=> 3
56
+ def size
57
+ @container.size
58
+ end
59
+
60
+ # Returns true if the queue is empty, false otherwise.
61
+ def empty?
62
+ @container.empty?
63
+ end
64
+
65
+ # Iterate over the Queue in FIFO order.
66
+ def each(&block)
67
+ @container.each_forward(&block)
68
+ end
69
+
70
+ end
71
+ end
72
72
  end