if RUBY_VERSION<"1.9" require 'mathn' def Prime.each(upper,&block) @primes=Prime.new @primes.each do |prime| break if prime > upper.to_i block.call(prime) end end else require 'prime' end require 'bigdecimal' require 'bigdecimal/math' # Useful additions to Math module Distribution module MathExtension # Factorization based on Prime Swing algorithm, by Luschny (the king of factorial numbers analysis :P ) # == Reference # * The Homepage of Factorial Algorithms. (C) Peter Luschny, 2000-2010 # == URL: http://www.luschny.de/math/factorial/csharp/FactorialPrimeSwing.cs.html class SwingFactorial attr_reader :result SmallOddSwing=[ 1, 1, 1, 3, 3, 15, 5, 35, 35, 315, 63, 693, 231, 3003, 429, 6435, 6435, 109395, 12155, 230945, 46189, 969969, 88179, 2028117, 676039, 16900975, 1300075, 35102025, 5014575,145422675, 9694845, 300540195, 300540195] def bitcount(n) bc = n - ((n >> 1) & 0x55555555); bc = (bc & 0x33333333) + ((bc >> 2) & 0x33333333); bc = (bc + (bc >> 4)) & 0x0f0f0f0f; bc += bc >> 8; bc += bc >> 16; bc = bc & 0x3f; bc end def initialize(n) if (n<20) naive_factorial(n) else @prime_list=[] exp2 = n - bitcount(n); @result= recfactorial(n)<< exp2 end end def recfactorial(n) return 1 if n<2 (recfactorial(n/2)**2) * swing(n) end def swing(n) return SmallOddSwing[n] if (n<33) sqrtN = Math.sqrt(n).floor count=0 Prime.each(n/3) do |prime| next if prime<3 if (prime<=sqrtN) q=n _p=1 while((q=(q/prime).truncate)>0) do if((q%2)==1) _p*=prime end end if _p>1 @prime_list[count]=_p count+=1 end else if ((n/prime).truncate%2==1) @prime_list[count]=prime count+=1 end end end prod=get_primorial((n/2).truncate+1,n) prod * @prime_list[0,count].inject(1) {|ac,v| ac*v} end def get_primorial(low,up) prod=1; Prime.each(up) do |prime| next if primen-k # # (n) n^k' (n)..(n-k+1) # ( ) = ---- = ------------ # (k) k! k! # def binomial_coefficient(n,k) return 1 if (k==0 or k==n) k=[k, n-k].min (((n-k+1)..n).inject(1) {|ac,v| ac * v}).quo(factorial(k)) # Other way to calcule binomial is this: # (1..k).inject(1) {|ac, i| (ac*(n-k+i).quo(i))} end # Approximate binomial coefficient, using gamma function. # The fastest method, until we fall on BigDecimal! def binomial_coefficient_gamma(n,k) return 1 if (k==0 or k==n) k=[k, n-k].min val=gamma(n+1) / (gamma(k+1)*gamma(n-k+1)) if (val.nan?) lg=lgamma(n+1) - (lgamma(k+1)+lgamma(n-k+1)) val=Math.exp(lg) # Crash again! We require BigDecimals if val.infinite? val=BigMath.exp(BigDecimal(lg.to_s),16) end end val end end end module Math include Distribution::MathExtension alias :lgamma :loggamma module_function :factorial, :beta, :gamma, :gosper, :loggamma, :lgamma, :binomial_coefficient, :binomial_coefficient_gamma, :regularized_beta_function, :incomplete_beta, :permutations, :rising_factorial , :fast_factorial end # Necessary on Ruby 1.9 module CMath # :nodoc: include Distribution::MathExtension module_function :factorial, :beta, :gosper, :loggamma, :binomial_coefficient, :binomial_coefficient_gamma, :regularized_beta_function, :incomplete_beta, :permutations, :rising_factorial, :fast_factorial end