large_binomials 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,82 @@
1
+ large_binomials
2
+ ===============
3
+
4
+ This gem mixes in some general methods to calculate binomials to `Integer`. In
5
+ addition, it also mixes in some methods to calculate large binomials, with
6
+ “large” being defined as having a result greater than `Float.MAX`. The main
7
+ `Integer#binomial(k)` method is then adjusted such that when the result is less
8
+ than or equal to `Float.MAX`, an `Integer` is returned, and a
9
+ `LargeBinomials::LargeFloat` otherwise. Continuing to use `Integer` makes the
10
+ calculation of binomials more and more expensive as the result becomes larger
11
+ and larger.
12
+
13
+ Installation
14
+ ------------
15
+
16
+ Do the following to install this gem:
17
+
18
+ 1. Make a clone of the repository
19
+ 2. Build the gem: `gem build large_binomials.gemspec`
20
+ 3. Install the gem: `gem install ./large_binomials-0.9.gem`
21
+
22
+ Usage
23
+ -----
24
+
25
+ The following code (also included in the project as
26
+ `bin/test_performance_binomial.rb`) shows how to use the library:
27
+
28
+ # encoding: utf-8
29
+ #
30
+ # Test the performance of the calculation of a binomial
31
+ #
32
+
33
+ require 'benchmark'
34
+ require 'large_binomials'
35
+
36
+ puts "Going to test the performance of the calculation of a binomial."
37
+
38
+ Samples = (1 .. 3).collect{ |i| 10 ** i}.collect{ |i| [1, 2, 3, 5].collect{ |j| j * i}}.flatten
39
+
40
+ Samples.each{ |n|
41
+ [1, n/100, n/10, n/2].each { |k|
42
+ puts "#{n} C #{k}"
43
+ c = 0
44
+ time = Benchmark.realtime { c = n.binomial(k) }
45
+ puts " binomial: #{time}s (#{c})"
46
+ time = Benchmark.realtime { c = n.float_binomial_by_product_of_divisions(k) }
47
+ puts " Π(/), float: #{time}s (#{c})"
48
+ time = Benchmark.realtime { c = n.float_binomial_by_division_of_products(k) }
49
+ puts " Π/Π, float: #{time}s (#{c})"
50
+ time = Benchmark.realtime { c = n.large_float_binomial_by_product_of_divisions(k) }
51
+ puts " Π(/), LargeFloat: #{time}s (#{c})"
52
+ time = Benchmark.realtime { c = n.large_float_binomial_by_division_of_products(k) }
53
+ puts " Π/Π, LargeFloat: #{time}s (#{c})"
54
+ time = Benchmark.realtime { c = n.binomial_by_division_of_parallel_products(k) }
55
+ puts " ∥Π/∥Π, integer: #{time}s (#{c})"
56
+ time = Benchmark.realtime { c = n.binomial_by_division_of_products(k) }
57
+ puts " Π/Π, integer: #{time}s (#{c})"
58
+ time = Benchmark.realtime { c = n.binomial_by_product_of_divisions(k) }
59
+ puts " Π(/), integer: #{time}s (#{c})"
60
+ time = Benchmark.realtime { c = n.binomial_by_pascals_triangle(k) }
61
+ puts " ∑, integer: #{time}s (#{c})"
62
+ }
63
+ }
64
+
65
+ License
66
+ -------
67
+
68
+ Library to calculate large binomials
69
+ Copyright (C) 2013 Filip van Laenen <f.a.vanlaenen@ieee.org>
70
+
71
+ This program is free software: you can redistribute it and/or modify
72
+ it under the terms of the GNU General Public License as published by
73
+ the Free Software Foundation, either version 3 of the License, or
74
+ (at your option) any later version.
75
+
76
+ This program is distributed in the hope that it will be useful,
77
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
78
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
79
+ GNU General Public License for more details.
80
+
81
+ You should have received a copy of the GNU General Public License
82
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,45 @@
1
+ # Ruby Library to Calculate Large Binomials
2
+ # Copyright © 2013 Filip van Laenen <f.a.vanlaenen@ieee.org>
3
+ #
4
+ # This file is part of the Ruby Library to Calculate Large Binomials.
5
+ #
6
+ # The Ruby Library to Calculate Large Binomials is free software: you can
7
+ # redistribute it and/or modify it under the terms of the GNU General Public
8
+ # License as published by the Free Software Foundation, either version 3 of the
9
+ # License, or (at your option) any later version.
10
+ #
11
+ # The Ruby Library to Calculate Large Binomials is distributed in the hope that
12
+ # it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14
+ # Public License for more details.
15
+ #
16
+ # You can find a copy of the GNU General Public License in /LICENSE
17
+ #
18
+
19
+ require 'rspec/core/rake_task'
20
+
21
+ desc 'Default: run specs.'
22
+ task :default => :spec
23
+
24
+ desc 'Run specs'
25
+ RSpec::Core::RakeTask.new do |t|
26
+ t.pattern = "./spec/**/*_spec.rb"
27
+ end
28
+
29
+ def find_integer_methods
30
+ require_relative 'lib/core_ext/integer'
31
+ integer_methods = Integer.instance_methods(false)
32
+ integer_methods.select!{ | m | 1.method(m).source_location != nil }
33
+ integer_methods.select!{ | m | 1.method(m).source_location[0].include? 'large_binomials'}
34
+ integer_methods.collect{ | m | "::Integer##{m}"}
35
+ end
36
+
37
+ desc 'Run Mutant'
38
+ task :mutant do
39
+ require 'mutant'
40
+ find_integer_methods
41
+ status = Mutant::CLI.run(['::LargeBinomials*', find_integer_methods, '--rspec-unit'].flatten)
42
+ if status.nonzero?
43
+ abort 'Mutant task is not successful'
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/ruby
2
+ # encoding: utf-8
3
+ #
4
+ # Test the performance of the calculation of a binomial
5
+ #
6
+
7
+ require 'benchmark'
8
+ require 'large_binomials'
9
+
10
+ puts "Going to test the performance of the calculation of a binomial."
11
+
12
+ Samples = (1 .. 3).collect{ |i| 10 ** i}.collect{ |i| [1, 2, 3, 5].collect{ |j| j * i}}.flatten
13
+
14
+ Samples.each{ |n|
15
+ [1, n/100, n/10, n/2].each { |k|
16
+ puts "#{n} C #{k}"
17
+ c = 0
18
+ time = Benchmark.realtime { c = n.binomial(k) }
19
+ puts " binomial: #{time}s (#{c})"
20
+ time = Benchmark.realtime { c = n.float_binomial_by_product_of_divisions(k) }
21
+ puts " Π(/), float: #{time}s (#{c})"
22
+ time = Benchmark.realtime { c = n.float_binomial_by_division_of_products(k) }
23
+ puts " Π/Π, float: #{time}s (#{c})"
24
+ time = Benchmark.realtime { c = n.large_float_binomial_by_product_of_divisions(k) }
25
+ puts " Π(/), LargeFloat: #{time}s (#{c})"
26
+ time = Benchmark.realtime { c = n.large_float_binomial_by_division_of_products(k) }
27
+ puts " Π/Π, LargeFloat: #{time}s (#{c})"
28
+ time = Benchmark.realtime { c = n.binomial_by_division_of_parallel_products(k) }
29
+ puts " ∥Π/∥Π, integer: #{time}s (#{c})"
30
+ time = Benchmark.realtime { c = n.binomial_by_division_of_products(k) }
31
+ puts " Π/Π, integer: #{time}s (#{c})"
32
+ time = Benchmark.realtime { c = n.binomial_by_product_of_divisions(k) }
33
+ puts " Π(/), integer: #{time}s (#{c})"
34
+ time = Benchmark.realtime { c = n.binomial_by_pascals_triangle(k) }
35
+ puts " ∑, integer: #{time}s (#{c})"
36
+ }
37
+ }
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Ruby Library to Calculate Large Binomials
4
+ # Copyright © 2013 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of the Ruby Library to Calculate Large Binomials.
7
+ #
8
+ # The Ruby Library to Calculate Large Binomials is free software: you can
9
+ # redistribute it and/or modify it under the terms of the GNU General Public
10
+ # License as published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # The Ruby Library to Calculate Large Binomials is distributed in the hope that
14
+ # it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16
+ # Public License for more details.
17
+ #
18
+ # You can find a copy of the GNU General Public License in /LICENSE
19
+ #
20
+
21
+ Gem::Specification.new do |gem|
22
+ gem.name = 'large_binomials'
23
+ gem.version = '1.0.0'
24
+ gem.authors = [ 'Filip van Laenen' ]
25
+ gem.email = [ 'f.a.vanlaenen@ieee.org' ]
26
+
27
+ gem.description = 'Large binomials'
28
+ gem.summary = 'Library to calculate large binomials'
29
+ gem.homepage = 'https://github.com/filipvanlaenen/large_binomials'
30
+ gem.license = 'GPL'
31
+
32
+ gem.require_paths = [ 'lib' ]
33
+ gem.files = `git ls-files`.split("\n")
34
+ gem.test_files = `git ls-files -- spec`.split("\n")
35
+ gem.extra_rdoc_files = %w[LICENSE README.md]
36
+
37
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Ruby Library to Calculate Large Binomials
4
+ # Copyright © 2013 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of the Ruby Library to Calculate Large Binomials.
7
+ #
8
+ # The Ruby Library to Calculate Large Binomials is free software: you can
9
+ # redistribute it and/or modify it under the terms of the GNU General Public
10
+ # License as published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # The Ruby Library to Calculate Large Binomials is distributed in the hope that
14
+ # it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16
+ # Public License for more details.
17
+ #
18
+ # You can find a copy of the GNU General Public License in /LICENSE
19
+ #
20
+
21
+ class Integer
22
+
23
+ def to_lf
24
+ LargeBinomials::LargeFloat.new(self)
25
+ end
26
+
27
+ def binomial(k)
28
+ binomial = breaking_binomial_by_product_of_divisions(k)
29
+ if binomial == nil
30
+ large_float_binomial_by_product_of_divisions(k)
31
+ else
32
+ binomial
33
+ end
34
+ end
35
+
36
+ # Copied from http://rosettacode.org/wiki/Evaluate_binomial_coefficients#Ruby (on 7 June 2013)
37
+ def binomial_by_product_of_divisions(k)
38
+ (0...k).inject(1) do |m,i|
39
+ (m * (self - i)) / (i + 1)
40
+ end
41
+ end
42
+
43
+ # Breaking version of binomial_by_product_of_divisions
44
+ def breaking_binomial_by_product_of_divisions(k)
45
+ (0...k).inject(1) do |m,i|
46
+ if m > Float::MAX
47
+ return nil
48
+ end
49
+ (m * (self - i)) / (i + 1)
50
+ end
51
+ end
52
+
53
+ # LargeFloat version of binomial_by_product_of_divisions
54
+ def large_float_binomial_by_product_of_divisions(k)
55
+ (0...k).inject(1.to_lf) do |m,i|
56
+ (m * (self - i)) / (i + 1)
57
+ end
58
+ end
59
+
60
+ # Float version of binomial_by_product_of_divisions
61
+ def float_binomial_by_product_of_divisions(k)
62
+ (0...k).inject(1.to_f) do |m,i|
63
+ (m * (self - i)) / (i + 1)
64
+ end
65
+ end
66
+
67
+ # From http://rosettacode.org/wiki/Evaluate_binomial_coefficients#Ruby (on 7 June 2013)
68
+ def binomial_by_division_of_products(k)
69
+ # n!/(n-k)!
70
+ pTop = (self-k+1 .. self).inject(1, &:*)
71
+ # k!
72
+ pBottom = (2 .. k).inject(1, &:*)
73
+ pTop / pBottom
74
+ end
75
+
76
+ # Parallel version of binomial_by_division_of_products
77
+ # Note that threads in Ruby 1.9 don't spread over multiple processors, so
78
+ # due to context switching this parallel version will often be a bit slower
79
+ # than the sequential one.
80
+ def binomial_by_division_of_parallel_products(k)
81
+ # n!/(n-k)!
82
+ top_thread = Thread.new do
83
+ Thread.current[:output] = (self-k+1 .. self).inject(1, &:*)
84
+ end
85
+ # k!
86
+ bottom_thread = Thread.new do
87
+ Thread.current[:output] = (2 .. k).inject(1, &:*)
88
+ end
89
+ top_thread.join
90
+ bottom_thread.join
91
+ top_thread[:output] / bottom_thread[:output]
92
+ end
93
+
94
+ # LargeFloat version of binomial_by_division_of_products
95
+ def large_float_binomial_by_division_of_products(k)
96
+ # n!/(n-k)!
97
+ pTop = (self-k+1 .. self).inject(1.to_lf, &:*)
98
+ # k!
99
+ pBottom = (2 .. k).inject(1.to_lf, &:*)
100
+ pTop / pBottom
101
+ end
102
+
103
+ # Float version of binomial_by_division_of_products
104
+ def float_binomial_by_division_of_products(k)
105
+ # n!/(n-k)!
106
+ pTop = (self-k+1 .. self).inject(1.to_f, &:*)
107
+ # k!
108
+ pBottom = (2 .. k).inject(1.to_f, &:*)
109
+ pTop / pBottom
110
+ end
111
+
112
+ # Copied from http://www.brpreiss.com/books/opus8/html/page459.html (on 7 June 2013)
113
+ def binomial_by_pascals_triangle(k)
114
+ b = []
115
+ b[0] = 1
116
+ for i in 1 .. self
117
+ b[i] = 1
118
+ j = i - 1
119
+ while j > 0
120
+ b[j] += b[j - 1]
121
+ j -= 1
122
+ end
123
+ end
124
+ b[k]
125
+ end
126
+
127
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Ruby Library to Calculate Large Binomials
4
+ # Copyright © 2013 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of the Ruby Library to Calculate Large Binomials.
7
+ #
8
+ # The Ruby Library to Calculate Large Binomials is free software: you can
9
+ # redistribute it and/or modify it under the terms of the GNU General Public
10
+ # License as published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # The Ruby Library to Calculate Large Binomials is distributed in the hope that
14
+ # it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16
+ # Public License for more details.
17
+ #
18
+ # You can find a copy of the GNU General Public License in /LICENSE
19
+ #
20
+
21
+ # Library namespace
22
+ module LargeBinomials
23
+ end
24
+
25
+ require 'core_ext/integer'
26
+ require 'large_binomials/large_float'
@@ -0,0 +1,129 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Ruby Library to Calculate Large Binomials
4
+ # Copyright © 2013 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of the Ruby Library to Calculate Large Binomials.
7
+ #
8
+ # The Ruby Library to Calculate Large Binomials is free software: you can
9
+ # redistribute it and/or modify it under the terms of the GNU General Public
10
+ # License as published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # The Ruby Library to Calculate Large Binomials is distributed in the hope that
14
+ # it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16
+ # Public License for more details.
17
+ #
18
+ # You can find a copy of the GNU General Public License in /LICENSE
19
+ #
20
+
21
+ module LargeBinomials
22
+ class LargeFloat
23
+ include Comparable
24
+
25
+ attr_accessor :exponent, :mantissa
26
+
27
+ def initialize(m, e = 0)
28
+ @mantissa = m.to_f
29
+ @exponent = e
30
+ end
31
+
32
+ def clone
33
+ LargeFloat.new(@mantissa, @exponent)
34
+ end
35
+
36
+ def normalize
37
+ unless @mantissa == 0
38
+ normalization = Math.log10(@mantissa).floor
39
+ @mantissa /= 10 ** normalization
40
+ @exponent += normalization
41
+ end
42
+ end
43
+
44
+ def *(x)
45
+ if (x.instance_of? LargeFloat)
46
+ multiply_with_large_float(x)
47
+ else
48
+ multiply_with_numeric(x)
49
+ end
50
+ end
51
+
52
+ def multiply_with_large_float(lf)
53
+ product = self * lf.mantissa
54
+ product.exponent += lf.exponent
55
+ product
56
+ end
57
+
58
+ def multiply_with_numeric(i)
59
+ product = clone
60
+ new_mantissa = product.mantissa * i
61
+ if new_mantissa == Float::INFINITY
62
+ product.normalize
63
+ end
64
+ product.mantissa *= i
65
+ product
66
+ end
67
+
68
+ def /(x)
69
+ if (x.instance_of? LargeFloat)
70
+ divide_by_large_float(x)
71
+ else
72
+ divide_by_numeric(x)
73
+ end
74
+ end
75
+
76
+ def divide_by_large_float(lf)
77
+ quotient = self / lf.mantissa
78
+ quotient.exponent -= lf.exponent
79
+ quotient
80
+ end
81
+
82
+ def divide_by_numeric(n)
83
+ quotient = clone
84
+ quotient.mantissa /= n
85
+ quotient
86
+ end
87
+
88
+ def new_mantissa_for_addition(lf)
89
+ @mantissa + lf.mantissa * 10 ** (lf.exponent - @exponent)
90
+ end
91
+
92
+ def +(lf)
93
+ if self < lf
94
+ lf + self
95
+ else
96
+ LargeFloat.new(new_mantissa_for_addition(lf), @exponent)
97
+ end
98
+ end
99
+
100
+ def superscript(i)
101
+ s = i.to_s
102
+ s = s.gsub(/1/, '¹')
103
+ s = s.gsub(/2/, '²')
104
+ s = s.gsub(/3/, '³')
105
+ s = s.gsub(/4/, '⁴')
106
+ s = s.gsub(/5/, '⁵')
107
+ s = s.gsub(/6/, '⁶')
108
+ s = s.gsub(/7/, '⁷')
109
+ s = s.gsub(/8/, '⁸')
110
+ s = s.gsub(/9/, '⁹')
111
+ s.gsub(/0/, '⁰')
112
+ end
113
+
114
+ def to_s
115
+ normalize
116
+ "#{@mantissa}×10#{superscript(@exponent)}"
117
+ end
118
+
119
+ def <=>(lf)
120
+ normalize
121
+ lf.normalize
122
+ if @exponent == lf.exponent
123
+ @mantissa - lf.mantissa
124
+ else
125
+ @exponent - lf.exponent
126
+ end
127
+ end
128
+ end
129
+ end