large_binomials 1.0.0

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.
@@ -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