binary_search 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
1
+ h1. Binary Search for Ruby's Arrays
2
+
3
+ One incredibly handy algorithm that is missing from Ruby's Array class is the binary search. If we *know* for *absolute certain* that the array we're working with is sorted you can use a binary search to search through the array much much more quickly than a linear search, which would be performed with index or detect/find.
4
+
5
+ h2. Usage
6
+
7
+ There are two methods defined by this gem. binary_search and binary_index. There are two versions of both of those methods. You can use the native version by requiring 'binary_search/native' or use the pure Ruby version with 'binary_search/pure'.
8
+
9
+ <pre>
10
+ <code>
11
+ require 'binary_search/native'
12
+
13
+ x = [5,1,6,7,2,6,4,2,6,1,6,1,1,8,3,5,2].sort
14
+ puts x.binary_index(5)
15
+ #=> 10
16
+
17
+ target = 4
18
+ y = [[1,:a], [2,:b], [3,:c], [4,:d]]
19
+ puts x.binary_search { |v| target <=> v[0] }
20
+ #=> [4,:d]
21
+ </code>
22
+ </pre>
23
+
24
+ So the method 'binary_index' does the same thing that 'index' does: returns the index of a matching element. It should be noted that 'index' returns the *first* instance of a matching element. 'binary_index' is not guaranteed to return the first. It should also be noted, again, that this will only work if the array is sorted correctly. If it's not weird crap will happen.
25
+
26
+ 'binary_search' is similar to 'find' or 'detect' from Ruby's normal arsenal. The difference is that rather than the block needing to return a boolean, it needs to return the usual output from the '<=>' operator. (1 for >, -1 for <, and 0 for ==). This is obviously because we need to know whether the element being evaluated is greater than, less than, or equal to the value we're actually looking for.
27
+
28
+
29
+ h2. Benchmarks
30
+
31
+ Need proof? Howsabout some benchmarks:
32
+
33
+ <pre>
34
+ == Benchmark Ruby's builtin :index method vs. a pure Ruby binary search method
35
+
36
+ Benchmark for 2000000 iterations searching through an array of 5 elements.
37
+ user system total real
38
+ Index: 1.320000 0.010000 1.330000 ( 1.320691)
39
+ Ruby BI: 8.700000 0.010000 8.710000 ( 8.774825)
40
+
41
+ Benchmark for 1000000 iterations searching through an array of 10 elements.
42
+ user system total real
43
+ Index: 0.990000 0.000000 0.990000 ( 0.996690)
44
+ Ruby BI: 6.000000 0.010000 6.010000 ( 6.043375)
45
+
46
+ Benchmark for 1000000 iterations searching through an array of 100 elements.
47
+ user system total real
48
+ Index: 6.050000 0.020000 6.070000 ( 6.091887)
49
+ Ruby BI: 10.250000 0.010000 10.260000 ( 10.318961)
50
+
51
+ Benchmark for 100000 iterations searching through an array of 1000 elements.
52
+ user system total real
53
+ Index: 6.120000 0.010000 6.130000 ( 6.155703)
54
+ Ruby BI: 1.480000 0.010000 1.490000 ( 1.493227)
55
+
56
+ Benchmark for 10000 iterations searching through an array of 10000 elements.
57
+ user system total real
58
+ Index: 5.880000 0.010000 5.890000 ( 5.916098)
59
+ Ruby BI: 0.200000 0.000000 0.200000 ( 0.206104)
60
+
61
+ Benchmark for 1000 iterations searching through an array of 100000 elements.
62
+ user system total real
63
+ Index: 5.950000 0.010000 5.960000 ( 6.005916)
64
+ Ruby BI: 0.030000 0.000000 0.030000 ( 0.027823)
65
+
66
+
67
+
68
+ == Benchmark Ruby's builtin :index method vs. a native binary search method
69
+
70
+ Benchmark for 2000000 iterations searching through an array of 5 elements.
71
+ user system total real
72
+ Index: 1.360000 0.000000 1.360000 ( 1.369248)
73
+ Native BI: 1.140000 0.010000 1.150000 ( 1.144739)
74
+
75
+ Benchmark for 1000000 iterations searching through an array of 10 elements.
76
+ user system total real
77
+ Index: 0.960000 0.000000 0.960000 ( 0.971568)
78
+ Native BI: 0.630000 0.000000 0.630000 ( 0.637069)
79
+
80
+ Benchmark for 1000000 iterations searching through an array of 100 elements.
81
+ user system total real
82
+ Index: 6.150000 0.010000 6.160000 ( 6.192804)
83
+ Native BI: 0.810000 0.000000 0.810000 ( 0.816337)
84
+
85
+ Benchmark for 100000 iterations searching through an array of 1000 elements.
86
+ user system total real
87
+ Index: 6.170000 0.020000 6.190000 ( 6.216637)
88
+ Native BI: 0.110000 0.000000 0.110000 ( 0.111025)
89
+
90
+ Benchmark for 10000 iterations searching through an array of 10000 elements.
91
+ user system total real
92
+ Index: 5.980000 0.010000 5.990000 ( 6.033161)
93
+ Native BI: 0.010000 0.000000 0.010000 ( 0.013183)
94
+
95
+ Benchmark for 1000 iterations searching through an array of 100000 elements.
96
+ user system total real
97
+ Index: 5.920000 0.020000 5.940000 ( 5.972206)
98
+ Native BI: 0.000000 0.000000 0.000000 ( 0.001602)
99
+ </pre>
100
+
101
+ So, your array must be fairly large (between 100 and 1000 elements) for the Ruby version of binary_index to be faster than Ruby's builtin index method. However, even for arrays as small as 5 elements, the native version of the binary_index method is faster than Ruby's index. However, for very large sized Arrays, both the the pure and the native version are much much much faster than the builtin method.
102
+
@@ -0,0 +1,4 @@
1
+ ---
2
+ patch: 1
3
+ major: 0
4
+ minor: 1
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile 'binary_search'
@@ -0,0 +1 @@
1
+ raise "require 'binary_search/pure' for the pure Ruby implementation or 'binary_search/native' for the C version."
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../../ext/binary_search'
@@ -0,0 +1,32 @@
1
+ class Array
2
+ def binary_index(target)
3
+ binary_chop { |v| target <=> v }
4
+ end
5
+
6
+ def binary_search(&block)
7
+ index = binary_chop(&block)
8
+ index ? self[index] : nil
9
+ end
10
+
11
+ private
12
+
13
+ def binary_chop(&block)
14
+ upper = self.size - 1
15
+ lower = 0
16
+
17
+ while(upper >= lower) do
18
+ idx = lower + (upper - lower) / 2
19
+ comp = yield self[idx]
20
+
21
+ if comp == 0
22
+ return idx
23
+ elsif comp > 0
24
+ lower = idx + 1
25
+ else
26
+ upper = idx - 1
27
+ end
28
+ end
29
+ nil
30
+ end
31
+ end
32
+
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binary_search
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
+ platform: ruby
11
+ authors:
12
+ - Tyler McMullen
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2008-12-29 00:00:00 -08:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Binary search and index methods for Ruby Arrays.
22
+ email: tbmcmullen@gmail.com
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/extconf.rb
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - README.textile
31
+ - VERSION.yml
32
+ - ext/extconf.rb
33
+ - lib/binary_search/native.rb
34
+ - lib/binary_search/pure.rb
35
+ - lib/binary_search.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/tyler/binary_search
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ - ext
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ segments:
51
+ - 0
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.6
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: Binary search and index methods for Ruby Arrays.
67
+ test_files: []
68
+